|
ขอนำเสนอคลาส MySQL_Connection ตัวช่วยที่จะทำให้คุณเขียนโปรแกรมติดต่อฐานข้อมูล MySQL ได้ง่ายขึ้น (ใช้ MySQLi) |
ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page
ขอนำเสนอคลาส MySQL_Connection ตัวช่วยที่จะทำให้คุณเขียนโปรแกรมติดต่อฐานข้อมูล MySQL ได้ง่ายขึ้น (ใช้ MySQLi)
อย่างที่ทราบกันแล้วว่า ในขณะนี้ฟังก์ชั่น MySQL Functions ได้มีสถานะ deprecated ใน PHP5.5 แล้ว
จึงมึการแนะนำกันทั่วไปว่าให้เปลี่ยนมาใช้ MySQLi หรือ PDO
ซึ่งผมเองก็ได้เขียนบทความอธิบายการใช้ฟังก์ชั่น MySQLi เบื้องต้นไว้แล้วด้วย
แต่ปัญหาก็คือ ทั้งสองอย่างนั้นมีการใช้งานที่อาจจะดูยุ่งยากสำหรับมือใหม่ และไม่ได้ช่วยให้เขียน SQL ได้ปลอดภัยขึ้นหากไม่ใช้ feature ที่ค่อนข้างซับซ้อน เช่น prepared statement
การใช้ prepared statement เพื่อป้องกัน SQL Injection
$mysqli = mysqli_init();
$mysqli->real_connect('localhost', 'root', '', 'testdb');
// ให้ ? ใน query เป็นตัวแปรที่มาจากข้างนอก
$stmt = $mysqli->prepare("SELECT * FROM `games` LIMIT ?");
// กำหนดให้ ? เป็นชนิด integer และ "เชื่อม" (bind) เข้ากับตัวแปร $limit
$stmt->bind_param('i', $limit);
// เมื่อกำหนดค่า ? ใน query ก็จะมีค่าเท่ากับ $limit
$limit = $_POST['limit'];
// ทำการรัน query
$stmt->execute();
// อ่าน result ที่ได้จากการรัน
$result = $stmt->get_result();
// ดึงข้อมูลแถวแรก
$row = $result->fetch_assoc();
// แสดงผล
print_r($row);
ซึ่งจะเห็นว่า มันค่อนข้างซับซ้อน โดยเฉพาะสำหรับมือใหม่
ผมจึงอยากขอนำเสนอตัวช่วย นั่นก็คือคลาส MySQL_Connection
เป็นคลาสที่ช่วยจัดการ query ที่ผมเขียนไว้ใช้เองเมื่อนานมาแล้ว โดยใช้ฟังก์ชั่น MySQLi เป็นฐาน
เป็นคลาสที่จำกัดความสามารถของ MySQLi ให้เหลือเพียงสิ่งที่ใช้เป็นประจำ
และช่วย escape ค่าที่มาจากผู้ใช้ หรือการ fetch ข้อมูลลง array ในการเรียกครั้งเดียว
การใช้ MySQL_Connection เพื่อป้องกัน SQL Injection เปรียบเทียบกับตัวอย่างก่อนหน้า
$mysql = new MySQL_Connection('localhost', 'root', '', 'testdb');
// ทำการแทนที่ %n ด้วยค่าของ $_POST['limit'] และดำเนินการรัน query พร้อมทั้งอ่านแถวที่ได้ลงตัวแปร $rows
$rows = $mysql->queryAndFetchAll("SELECT * FROM `games` LIMIT %n", $_POST['limit']);
// แสดงผลแถวแรก
print_r($rows[0]);
และยังช่วย debug เพราะจะจบการทำงานทันทีหากไม่มีการดักจับ exception เมื่อเกิด error
พร้อมทั้งแสดง error message และ query ที่มีปัญหา
การนำไปใช้งานก็แค่ copy โค้ดข้างล่างนี้ไปบันทึกเป็นไฟล์ ตั้งชื่อตามใจชอบครับ (แต่ผมใช้ชื่อว่า mysql.php)
แล้วเรียกใช้งานด้วยการ require หรือ include ตามปกติครับ
แต่หากใครที่ต้องการศึกษาฟังก์ชั่น MySQLi ด้วยตัวเอง ไม่อยากใช้คลาสของผม
ลองดูบทความนี้ เลิกใช้ฟังก์ชั่น mysql_xxxxx() ที่ล้าสมัย (deprecated) และเปลี่ยนมาใช้ MySQLi กันดีกว่า ครับ (เขียนโดยผมเองเหมือนกัน)
ซึ่งก็อยากให้ลองอ่านดู เพราะถ้าต้องการเปลี่ยนจากฟังก์ชั่น MySQL แบบเก่า และหันมาใช้ MySQLi
บทความดังกล่าวจะตรงประเด็นมากกว่าครับ
MySQL_Connection
<?php
/**
* MySQL_Connection
* คลาสที่จะช่วยให้คุณเขียนโปรแกรมเชื่อมต่อกับฐาน MySQL ได้สะดวกและปลอดภัยขึ้น
* ทั้งยังง่ายต่อการ debug เพราะจะจบการทำงานทันที หากมีข้อผิดพลาดเกิดขึ้นใน query
* พร้อมทั้งแสดง error message และ query ที่มีปัญหาให้
* Copyright (c) 2013, phpinfo.in.th (http://www.phpinfo.in.th)
*/
final class MySQL_Connection extends MySQL_Abstract
{
protected $connection;
public function __construct()
{
$this->connection = mysqli_init();
if (func_num_args() > 2) {
$args = func_get_args();
if (isset($args[3])) {
$this->connect($args[0], $args[1], $args[2], $args[3]);
} else {
$this->connect($args[0], $args[1], $args[2]);
}
}
}
protected function replaceStringCallback($matches, $newData = null)
{
static $data, $i;
if (!isset($matches)) {
$data = $newData;
$i = 0;
return;
} elseif (isset($matches[1])) {
$null = isset($matches[1][1]);
if (isset($matches[2])) {
$value = isset($data[$matches[2]]) ? $data[$matches[2]] : null;
} else {
$value = isset($data[$i]) ? $data[$i] : null;
++$i;
}
if (is_array($value)) {
switch ($matches[1][0]) {
case '%':
return implode(',', $value);
case 'q':
foreach ($value as &$v) {
$v = implode(
'`.`',
explode('.', preg_replace('/`/u', '``', $v))
);
}
return '`' . implode('`,`', $value) . '`';
case 'b':
foreach ($value as &$v) {
$v = $null && $v === null
? 'NULL'
: ((bool) $v ? 'TRUE' : 'FALSE');
}
return implode(',', $value);
case 'n':
foreach ($value as &$v) {
$v = $null && $v === null
? 'NULL'
: (is_numeric($v) ? $v : 0);
}
return implode(', ', $value);
case 's':
foreach ($value as &$v) {
$v = $null && $v === null
? 'NULL'
: $this->escapeString($v);
}
return '\'' . implode('\',\'', $value) . '\'';
}
} else {
switch ($matches[1][0]) {
case '%':
return $value;
case 'q':
return '`'
. implode(
'`.`',
explode('.', preg_replace('/`/u', '``', $value))
)
. '`';
case 'b':
return $null && $value === null
? 'NULL'
: ((bool) $value ? 'TRUE' : 'FALSE');
case 'n':
return $null && $value === null
? 'NULL'
: (is_numeric($value) ? $value : 0);
case 's':
return $null && $value === null
? 'NULL'
: '\'' . $this->escapeString($value) . '\'';
}
}
}
return $matches[0];
}
public function __get($name)
{
switch ($name) {
case 'charset':
$info = $this->connection->get_charset();
return $info->charset;
case 'errorCode':
return $this->connection->errno;
case 'errorMessage':
return $this->connection->error;
default:
break;
}
return isset($this->$name) ? $this->$name : null;
}
public function __set($name, $value)
{
switch ($name) {
case 'charset':
$this->connection->set_charset($value);
break;
case 'errorCode':
case 'errorMessage':
break;
default:
$this->$name = $value;
break;
}
}
public function __isset($name)
{
static $names = array(
'charset' => true,
'errorCode' => true,
'errorMessage' => true,
);
return isset($names[$name]);
}
public function connect($host, $username, $password, $dbname = null)
{
if (!@$this->connection->real_connect(
$host, $username, $password, $dbname
)
) {
throw new MySQL_Exception($this->connection->connect_error);
}
}
public function close()
{
$this->connection->close();
}
public function query($query, $params = null)
{
$result = $this->connection->query(
isset($params)
? $query = $this->replaceString($query, $params)
: $query
);
if ($result === false) {
throw new MySQL_Exception($this->connection->error, $query);
}
return true;
}
public function queryAndFetch($query, $params = null, $columnKey = null)
{
if (($result = $this->queryResult($query, $params))) {
$row = $result->fetch($columnKey);
$result->free();
return $row;
}
}
public function queryAndFetchAll($query, $params = null, $columnKey = null,
$indexKey = null
) {
if (($result = $this->queryResult($query, $params))) {
return $result->fetchAll($columnKey, $indexKey);
}
}
public function queryValue($query, $params = null)
{
if (($result = $this->queryResult($query, $params))) {
$row = $result->fetch();
$result->free();
return $row[key($row)];
}
}
public function queryResult($query, $params = null)
{
$result = $this->connection->query(
isset($params)
? $query = $this->replaceString($query, $params)
: $query
);
if ($result === false) {
throw new MySQL_Exception($this->connection->error, $query);
} elseif ($result === true) {
return;
}
return $this->createResult($result);
}
public function escapeString($value)
{
return $this->connection->real_escape_string($value);
}
public function replaceString()
{
$args = func_get_args();
$query = array_shift($args);
if (count($args) === 1) {
$args = is_array($args[0]) ? $args[0] : array($args[0]);
}
$this->replaceStringCallback(null, $args);
return preg_replace_callback(
'/
\x27(?>\x5c\x5c\x27|\x27\x27|[^\x27])*\x27#\x63\x6f\x6f\x6b\x69\x70\x68\x70
|\x22(?>\x5c\x5c\x22|\x22\x22|[^\x22])*\x22#\x63\x6f\x6f\x6b\x69\x70\x68\x70
|\x60(?>\x60\x60|[^\x60])*\x60#\x63\x6f\x6f\x6b\x69\x70\x68\x70
|\x25#\x63\x6f\x6f\x6b\x69\x70\x68\x70
(\x25|\x71|(?>\x73|\x62|\x6e)\x6e?)?#\x63\x6f\x6f\x6b\x69\x70\x68\x70
(?>\x5b([^\x5d]+)\x5d)?#\x63\x6f\x6f\x6b\x69\x70\x68\x70
|\x25#\x63\x6f\x6f\x6b\x69\x70\x68\x70
/ux',
array($this, 'replaceStringCallback'),
$query
);
}
public function ping()
{
$this->connection->ping();
}
}
final class MySQL_Result extends MySQL_Abstract
{
protected $result;
protected function __construct($result)
{
$this->result = $result;
}
public function __destruct()
{
$this->free();
}
public function __get($name)
{
switch ($name) {
case 'numRows':
return isset($this->result) ? $this->result->num_rows : 0;
default:
break;
}
return isset($this->$name) ? $this->$name : '';
}
public function __set($name, $value)
{
switch ($name) {
case 'numRows':
break;
default:
$this->$name = $value;
break;
}
}
public function fetch($columnKey = null)
{
if (!isset($this->result)) {
return;
}
if (!($row = $this->result->fetch_assoc())) {
$this->free();
}
if ($row) {
return isset($columnKey) ? $row[$columnKey] : $row;
}
}
public function fetchAll($columnKey = null, $indexKey = null)
{
if (!isset($this->result)) {
return;
}
$rows = array();
if (isset($columnKey)) {
if (isset($indexKey)) {
while (($row = $this->result->fetch_assoc())) {
$rows[$row[$indexKey]] = $row[$columnKey];
}
} else {
while (($row = $this->result->fetch_assoc())) {
$rows[] = $row[$columnKey];
}
}
} elseif (isset($indexKey)) {
while (($row = $this->result->fetch_assoc())) {
$rows[$row[$indexKey]] = $row;
}
} else {
while (($row = $this->result->fetch_assoc())) {
$rows[] = $row;
}
}
$this->free();
return $rows;
}
public function free()
{
if (isset($this->result)) {
$this->result->free();
unset($this->result);
}
}
}
final class MySQL_Exception extends Exception
{
private $query;
public function __construct($message = null, $query = null)
{
parent::__construct($message);
$this->query = $query;
set_exception_handler(array($this, 'displayError'));
}
public function displayError($exception = null)
{
if (!$exception) {
return;
}
if ($exception !== $this) {
throw $exception;
}
$t = $exception->getTrace();
$traces = array();
foreach ($t as $v) {
$class = isset($v['class']) ? $v['class'] : '';
if ($class === 'MySQL_Connection') {
continue;
}
$traces[] = htmlspecialchars(
"$v[file]($v[line]): "
. $class
. (isset($v['type']) ? $v['type'] : '')
. "$v[function]()"
);
}
$message = array('<h1>' . get_class($exception) . '</h1><hr />');
if ($exception->getMessage()) {
$message[] = '<p>' . htmlspecialchars($exception->getMessage()) . '</p><hr />';
}
if ($this->query) {
$message[] = '<pre>' . htmlspecialchars($this->query) . '</pre><hr />';
}
if ($traces) {
$message[] = '<ul><li>' . implode("</li><li>", $traces) . '</li></ul>';
}
$message = implode($message);
if (headers_sent()) {
echo $message;
exit;
}
while (ob_get_level()) {
ob_end_clean();
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php echo get_class($exception); ?> Exception</title>
<style type="text/css">
/*<![CDATA[*/
body {
background-color: #ffffff;
font-family: Consolas;
color: #000000;
}
pre, ul {
border: #6e2142 solid 1px;
border-radius: 6px;
background-color: #f0d4db;
font-family: Consolas;
font-size: 14px;
color: #6e2142;
white-space: pre-wrap;
}
pre {
padding: 8px;
}
ul {
padding-top: 8px;
padding-bottom: 8px;
}
/*]]>*/
</style>
</head>
<body>
<?php
echo $message;
?>
</body>
</html>
<?php
exit;
}
}
abstract class MySQL_Abstract
{
final protected function createResult($result)
{
return new MySQL_Result($result);
}
}
การเรียกใช้งาน
require 'mysql.php';
$mysql = new MySQL_Connection();
เชิญนำไปใช้งานและพัฒนาแก้ไขเพิ่มเติมได้เลยครับ
ขอเพียงให้เครดิตกันหน่อยนะครับ หากจะนำไปเผยแพร่ต่อ
Methods
connect()
ใช้เพื่อเชื่อมต่อกับ MySQL Server
connect($host, $username, $password [, $dbname])
$host ชื่อหรือ IP ของเซิร์ฟเวอร์ MySQL เช่น 'localhost', '127.0.0.1'
$username ชื่อผู้ใช้
$password รหัสผ่าน
$dbname (optional) ชื่อฐานข้อมูลที่จะใช้เป็นฐานข้อมูล default สำหรับทุกๆ query
require 'mysql.php';
$mysql = new MySQL_Connection();
$mysql->connect('localhost', 'root', 'password', 'default_db');
// บรรทัดข้างบนจะเทียบเท่ากับการเขียนแบบเก่า
// mysql_connect('localhost', 'root', 'password');
// mysql_select_db('default_db');
หรือ เรียกใช้ตั้งแต่ตอน new เลย
require 'mysql.php';
$mysql = new MySQL_Connection('localhost', 'root', 'password', 'default_db');
หมายเหตุ ควรจะเลือกฐานข้อมูล default ไว้ตลอด เพราะ MySQLi ไม่มีฟังก์ชั่นอย่าง mysql_select_db()
หากต้องการเปลี่ยนฐานข้อมูล default ต้องใช้ SQL USE
$mysql->query("USE `dbname`");
close()
ตัดการเชื่อมต่อกับ MySQL Server
$mysql->close();
query()
ส่งคำสั่ง SQL ไปให้เซิร์ฟเวอร์ MySQL ประมวลผล
query($query [, $params])
$query ชุดคำสั่ง SQL
$params (optional) ค่าของตัวแปรที่ใช้แทนที่ %xx ใน $query เป็น string หรือ array ก็ได้ (ดู replaceString() เพิ่มเติม)
method นี้คืนค่ากลับมาเป็น true เสมอ เหมาะสำหรับใช้กับพวกคำสั่ง INSERT, UPDATE, DELETE และคำสั่งอื่นๆ ที่ไม่มีข้อมูลคืนกลับมา
$mysql->query(
"UPDATE `table` SET `column1` = `column1` + %n",
4 // แทนที่ %n โดยกำหนดให้เป็นชนิดตัวเลข
);
// จะเท่ากับ UPDATE `table` SET `column1` = `column1` + 4
$mysql->query(
"UPDATE `table` SET `column2` = %s",
'CookiePHP' // แทนที่ %s โดยกำหนดให้เป็นสตริง
);
// จะเท่ากับ UPDATE `table` SET `column2` = 'CookiePHP'
$mysql->query(
"INSERT INTO `table` (`column1`, `column2`) VALUES (%n, %s)",
array(
500, // แทนที่ %n
"Cookie's cat" // แทนที่ %s
),
);
// จะเท่ากับ INSERT INTO `table` (`column1`, `column2`) VALUES (500, 'Cookie\'s cat')
queryAndFetch()
ส่งคำสั่ง SQL ไปให้เซิร์ฟเวอร์ MySQL ประมวลผล พร้อมกับดึงข้อมูลกลับมา 1 แถว (ถ้ามี)
แต่จะคืนค่ากลับมาเฉพาะ query ที่เป็นคำสั่งดึงข้อมูล เช่น SELECT หรือ SHOW เท่านั้น
คำสั่งอื่นๆ ที่ไม่มีการดึงข้อมูล เช่น INSERT หรือ UPDATE จะคืนค่ากลับมาเป็น null เสมอ
และหากไม่มีข้อมูล ก็จะคืนค่ากลับมาเป็น null เช่นกัน
queryAndFetch($query [, $params [, $columnKey]])
$query ชุดคำสั่ง SQL
$params (optional) ค่าของตัวแปรที่ใช้แทนที่ %xx ใน $query เป็น string หรือ array ก็ได้
$columnKey (optional) ชื่อคอลัมน์ในตารางที่ต้องการจะให้เป็นค่าที่คืนกลับไป
คืนค่ากลับมาเป็น array ของข้อมูลแถวในตาราง
แต่หากมีการกำหนด $columnKey จะคืนค่ากลับมาเป็น string
หรือ null ถ้าเป็นคำสั่งที่ไม่มีข้อมูลกลับมา หรือไม่มีข้อมูล
ตัวอย่าง
$first_game_found = $mysql->queryAndFetch("SELECT * FROM `games`");
// จะ SELECT เจอกี่แถวก็ตาม แต่จะดึงมาเฉพาะแถวแรกเท่านั้น
print_r($first_game_found);
ผลลัพธ์
Array
(
[id] => 1
[released] => 2012-09-20
[title] => Pro Evolution Soccer 2013
[price] => 1090
)
ตัวอย่างการใช้ $columnKey
$released = $mysql->queryAndFetch(
"SELECT * FROM `games` WHERE `title` = %s",
'Pro Evolution Soccer 2013', // แทนที่ %s
'released' // ดึงเฉพาะคอลัมน์ `released` ออกมา
);
// $released จะเป็น string ไม่ใช่ array สามารถใช้ได้ทันที
echo "This game was released on $released.";
ผลลัพธ์
This game was released on 2012-09-20.
queryAndFetchAll()
ส่งคำสั่ง SQL ไปให้เซิร์ฟเวอร์ MySQL ประมวลผล พร้อมกับดึงข้อมูลกลับมาทั้งหมด
แต่จะคืนค่ากลับมาเฉพาะ query ที่เป็นคำสั่งดึงข้อมูล เช่น SELECT หรือ SHOW เท่านั้น
คำสั่งอื่นๆ ที่ไม่มีการดึงข้อมูล เช่น INSERT หรือ UPDATE จะคืนค่ากลับมาเป็น null เสมอ
queryAndFetchAll($query [, $params, [, $columnKey [, $indexKey]]])
$query ชุดคำสั่ง SQL
$params (optional) ค่าของตัวแปรที่ใช้แทนที่ %xx ใน $query เป็น string หรือ array ก็ได้
$columnKey (optional) ชื่อคอลัมน์ในตารางที่ต้องการจะให้เป็นค่าที่อยู่ใน array แทนที่จะเป็น array ของแถว
$indexKey (optional) ชื่อคอลัมน์ในตารางที่ต้องการจะใช้เป็น key ของแต่ละแถวใน array ที่คืนกลับไป
ตัวอย่าง
$games = $mysql->queryAndFetchAll("SELECT * FROM `games` ORDER BY `released` LIMIT 5");
print_r($games);
ผลลัพธ์
Array
(
[0] => Array
(
[id] => 2
[released] => 2009-10-13
[title] => Uncharted 2: Among Thieves
[price] => 1790
)
[1] => Array
(
[id] => 21
[released] => 2009-10-13
[title] => Way of the Samurai 3
[price] => 1690
)
[2] => Array
(
[id] => 20
[released] => 2010-10-26
[title] => Grand Theft Auto 4: Complete Edition
[price] => 1790
)
[3] => Array
(
[id] => 19
[released] => 2011-02-22
[title] => Killzone 3
[price] => 1090
)
[4] => Array
(
[id] => 8
[released] => 2011-10-11
[title] => Ace Combat: Assault Horizon
[price] => 1090
)
)
ใช้ $columnKey เพื่อดึงเฉพาะข้อมูลคอลัมน์ที่ต้องการออกมา
$titles = $mysql->queryAndFetchAll(
"SELECT * FROM `games` LIMIT 10",
null, // ไม่ใช้
'title' // ดึงเฉพาะ title ออกมา ให้เป็นข้อมูลของแต่ละแถว
);
print_r($titles);
ผลลัพธ์
Array
(
[0] => Pro Evolution Soccer 2013
[1] => Uncharted 2: Among Thieves
[2] => UnCharted 3: Drake'S Deception
[3] => Syndicate
[4] => Dead Island: Riptide
[5] => Max Payne 3
[6] => Ninja Gaiden 3: Razor's Edge
[7] => Ace Combat: Assault Horizon
[8] => Rayman Origins
[9] => The Walking Dead: A TellTale Game Series
)
ใช้ $indexKey เพื่อทำให้ค่าของคอลัมน์ที่กำหนด เป็น key ของแต่ละแถว
$titles = $mysql->queryAndFetchAll(
"SELECT * FROM `games` LIMIT 3",
null, // ไม่ใช้
null, // ไม่ใช้
'title' // กำหนดให้ title เป็น key
);
print_r($titles);
ผลลัพธ์
Array
(
[Pro Evolution Soccer 2013] => Array
(
[id] => 1
[released] => 2012-09-20
[title] => Pro Evolution Soccer 2013
[price] => 1090
)
[Uncharted 2: Among Thieves] => Array
(
[id] => 2
[released] => 2009-10-13
[title] => Uncharted 2: Among Thieves
[price] => 1790
)
[UnCharted 3: Drake'S Deception] => Array
(
[id] => 3
[released] => 2011-11-01
[title] => UnCharted 3: Drake'S Deception
[price] => 1790
)
)
ใช้ทั้ง $columnKey และ $indexKey ร่วมกัน
$prices = $mysql->queryAndFetchAll(
"SELECT * FROM `games` LIMIT 10",
null, // ไม่ใช้
'price', // ให้ price เป็นข้อมูล
'title' // ให้ title เป็น key
);
print_r($prices);
ผลลัพธ์
Array
(
[Pro Evolution Soccer 2013] => 1090
[Uncharted 2: Among Thieves] => 1790
[UnCharted 3: Drake'S Deception] => 1790
[Syndicate] => 1090
[Dead Island: Riptide] => 1890
[Max Payne 3] => 1190
[Ninja Gaiden 3: Razor's Edge] => 1690
[Ace Combat: Assault Horizon] => 1090
[Rayman Origins] => 1390
[The Walking Dead: A TellTale Game Series] => 1390
)
queryResult()
เป็น method ที่จะคืนผลลัพธ์กลับมาเป็น instance ของคลาส MySQL_Result
ซึ่ง method นี้เพื่อให้สามารถเขียนโปรแกรมในลักษณะเดิมได้
และจะช่วยประหยัดหน่วยความจำในกรณีที่ต้องการอ่านข้อมูลจำนวนมาก ที่หากใช้ queryAndFetchAll() แล้วจะทำให้เปลืองหน่วยความจำ
queryResult($query [,$params])
$query ชุดคำสั่ง SQL
$params ค่าของตัวแปรที่ใช้แทนที่ %xx ใน $query เป็น string หรือ array ก็ได้ (ดู replaceString() เพิ่มเติม)
การเขียนโปรแกรมด้วยฟังก์ชั่น mysql_xxxxx() แบบเดิม
$result = mysql_query("SELECT * FROM `table`");
while (($row = mysql_fetch_assoc($result))) {
// do something
}
mysql_free_result($result);
การเขียนโปรแกรมด้วย queryResult()
$result = $mysql->queryResult("SELECT * FROM `table`");
while (($row = $result->fetch())) {
// do something
}
จะเห็นว่าความแตกต่างระหว่างแบบเก่ากับแบบใช้ queryResult() นั้นอยู่ที่ mysql_free_result($result);
ซึ่งจริงๆ แล้ว MySQL_Result ก็มี method free() ไว้เพื่อทำหน้าที่เดียวกันกับ mysql_free_result()
แต่ถ้าเขียนแบบนี้ก็ไม่จำเป็นต้องเรียก free() เพราะหากไม่มีข้อมูลให้อ่านแล้วการเรียกใช้ fetch() จะเรียก free() ให้เอง
คลาส MySQL_Result มี method แค่ 3 ตัวคือ fetch(), fetchAll() และ free()
fetch() จะคืนค่ากลับมาเป็นข้อมูลที่ยังเหลือทีละแถว หากไม่มีข้อมูลให้อ่านแล้ว จะเรียกใช้ free() และคืนค่ากลับมาเป็น null
fetchAll() จะคืนค่ากลับมาเป็นข้อมูลที่ยังเหลือทั้งหมด และเรียกใช้ free()
free() ทำลายข้อมูลที่ยังไม่ได้อ่านทิ้ง และคืนหน่วยความจำให้กับระบบ
และมี property เพียง 1 ตัวคือ $numRows
echo จำนวนแถวที่หาได้
$result = $mysql->queryResult("SELECT * `table`");
echo $result->numRows;
ping()
ส่งสัญญาณไปบอก MySQL Server ให้คงการเชื่อมต่อไว้ เพราะหากไม่ได้มีการส่ง query ไปในช่วงเวลาที่เซิร์ฟเวอร์กำหนด การเชื่อมต่อจะถูกตัดขาดลง
$mysql->ping();
escapeString()
escape ค่าที่ต้องการให้เป็นสตริงที่ปลอดภัยที่จะใช้ใน query
escapeString($value)
$value ค่าที่ต้องการ escape
ตัวอย่าง
$safe_string = $mysql->escapeString($_POST['username']);
replaceString()
แทนที่ตัวแปรลงในรูปแบบที่กำหนด เพื่อสร้าง query ที่ปลอดภัย ไร้ SQL Injection
รูปแบบการใช้งานจะดูคล้ายฟังก์ชั่น sprintf() อยู่มาก แต่จริงๆ แล้วไม่เหมือนกัน
ทุก query method ที่มี $params เป็น argument ก็จะมีการเรียกใช้ method นี้
replaceString($format, $params [, $...])
$format คือสตริงที่เป็น template มีรูปแบบที่กำหนดไว้ให้แทนที่
$params คือค่าที่ต้องการจะนำไปแทนที่ หากมีหลายค่า สามารถใช้เป็น array ได้
$... คือค่าอื่นๆ ที่จะนำไปแทนที่หากไม่ต้องการส่งค่าหลายๆ ค่าด้วย $params ที่เป็น array
ตัวอย่าง
$query = $mysql->replaceString(
"SELECT * FROM %q WHERE `released` > %s LIMIT %n",
'games', // แทนที่ %q
'2013-01-01', // แืทนที่ %s
10 // แทนที่ %n
);
echo $query;
ผลลัพธ์
SELECT * FROM `game` WHERE `released` > '2013-01-01' LIMIT 10
จากตัวอย่างข้างบน จะเห็นว่า ตัวแทนที่คือ %q, %s และ %n
ตัวแทนที่
method ที่เกี่ยวกับการ query ทั้งหมด
จะมี $params เป็น argument ซึ่งใช้เป็นตัวแปรที่จะแทนที่ตัวแทนที่ใน $query
ประโยชน์ของตัวแทนที่คือ สามารถสร้าง query ที่ปลอดภัยจาก SQL Injection ได้โดยสะดวก
และได้โค้ด SQL ที่อ่านง่าย สบายตา หาที่ผิดและแก้ไขได้ไม่ยาก
สร้าง INSERT query โดยใช้การแทนที่แบบเรียงลำดับ
$mysql->query(
"
INSERT INTO `games`
(`released`, `title`, `price`)
VALUES
(%s, %s, %s)
",
array(
$_POST['released'], // แทนที่ %s ตัวที่ 1
$_POST['title'], // แทนที่ %s ตัวที่ 2
$_POST['price'], // แทนที่ %s ตัวที่ 3
)
);
สร้าง UPDATE query โดยใช้การแทนที่แบบกำหนดชื่อ (ไม่ต้องเรียงลำดับ)
$mysql->query(
"
UPDATE `games`
SET
`released` = %s[RELEASED],
`title` = %s[TITLE],
`price` = %s[PRICE]
WHERE
`id` = %s[ID]
",
array(
'PRICE' => $_POST['price'], // แทนที่ %s[PRICE]
'TITLE' => $_POST['title'], // แทนที่ %s[TITLE]
'ID' => $_POST['id'], // แทนที่ %s[ID]
'RELEASED' => $_POST['released'], // แทนที่ %s[RELEASED]
)
);
สร้าง SELECT query โดยใช้การแทนที่แบบผสม
$rows = $mysql->queryAndFetchAll(
"
SELECT %q, %q, %q FROM `games`
WHERE `released` > %s[RELEASED] LIMIT %n[LIMIT]
",
array(
'RELEASED' => $_POST['released'], // แทนที่ %s[RELEASED]
'LIMIT' => $_POST['limit'], // แทนที่ %n[LIMIT]
'title', // แทนที่ %q ตัวที่ 1
'price', // แทนที่ %q ตัวที่ 2
'id', // แทนที่ %q ตัวที่ 3
)
);
ซึ่งรูปแบบของตัวแทนที่มีดังต่อไปนี้
%q
ทำให้ค่าที่นำมาแทนที่เป็น identifier เพื่อใช้เป็นชื่อฐานข้อมูล ชื่อตาราง หรือชื่อคอลัมน์ เพราะจะกลายเป็นสตริงที่ถูกครอบด้วย `
และยังมีลักษณะพิเศษคือ หากค่าที่ส่งมามี . อยู่ ก็จะแยกออกจากกันด้วย . ก่อนแล้วจึงครอบด้วย `
echo $mysql->replaceString("SELECT * FROM %q", 'mydb.table');
// SELECT * FROM `mydb`.`table`
echo $mysql->replaceString("SELECT * FROM `games` ORDER BY %q", 'games.id');
// SELECT * FROM `games` ORDER BY `games`.`id`
%s
ทำให้ค่าที่นำมาแทนที่เป็นชนิดสตริงที่ผ่านการ escape และครอบด้วย '
echo $mysql->replaceString("SELECT %s", "Cookie's cat");
// SELECT 'Cookie\'s cat'
echo $mysql->replaceString("SELECT %s", "Line 1
Line 2
Line 3");
// SELECT 'Line 1\nLine 2\nLine 3\n'
%n
ทำให้ค่าที่นำมาแทนที่เป็นชนิดตัวเลข หากไม่สามารถแปลงเป็นตัวเลขได้ จะกลายเป็น 0
echo $mysql->replaceString("SELECT %n", 1);
// SELECT 1
echo $mysql->replaceString("SELECT %n", '555');
// SELECT 555
echo $mysql->replaceString("SELECT %n", 'Hello World');
// SELECT 0
%b
ทำให้ค่าที่นำมาแทนที่เป็นชนิด Boolean คือจะเป็น TRUE หรือ FALSE เท่านั้น
เช่น 1 จะกลายเป็น TRUE และ 0 จะกลายเป็น FALSE
echo $mysql->replaceString("SELECT %b", true);
// SELECT TRUE
echo $mysql->replaceString("SELECT %b", 'Hello World');
// SELECT TRUE
echo $mysql->replaceString("SELECT %b", false);
// SELECT FALSE
echo $mysql->replaceString("SELECT %b", null);
// SELECT FALSE
%%
ใส่ค่าที่ต้องการเข้าไปตรงๆ เลย โดยไม่มีการ escape ใดใดทั้งสิ้น ไม่แนะนำให้ใช้หากไม่จำเป็นจริงๆ
echo $mysql->replaceString("SELECT %% `table`", 'COUNT(*) FROM');
// SELECT COUNT(*) FROM `table`
%?n
สำหรับ %s, %n และ %b สามารถมี n ต่อท้ายได้ โดยมีความหมายว่า "สามารถเป็น NULL"
echo $mysql->replaceString("SELECT %s", 'Hello');
// SELECT 'Hello'
echo $mysql->replaceString("SELECT %s", null);
// SELECT ''
echo $mysql->replaceString("SELECT %sn", null);
// SELECT NULL
การสร้าง list สำหรับ VALUES หรือ IN (...) ก็สามารถทำได้โดยง่าย
หากตัวแปรที่ส่งไปเป็น array ก็จะเชื่อมค่าเหล่านั้นด้วย , ให้อัตโนมัติ
สร้าง SELECT query
$ids = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
echo $mysql->replaceString(
"SELECT * FROM `games` WHERE `id` IN (%s)",
array($ids)
);
// SELECT * FROM `games` WHERE `id` IN ('1','2','3','4','5','6','7','8','9','10')
สร้าง INSERT query
$columns = array('title', 'price', 'released');
$values = array('Dragon Quest X', '2590', '2012-08-02');
$mysql->query(
"INSERT INTO `games` (%q) VALUES (%s)",
array($columns, $values)
);
// INSERT INTO `games` (`title`,`price`,`released`) VALUES ('Dragon Quest X','2590','2012-08-02')
Properties
$charset
encoding ปัจจุบันของการเชื่อมต่อ
กำหนด charset ของการเชื่อมต่อให้เป็น UTF-8 เทียบเท่ากับการใช้ SET NAMES utf8
$mysql->charset = 'utf8';
$errorCode
$errorMessage
หมายเลขความผิดพลาด (error code) และข้อความแสดงความผิดพลาด (error message) ที่เกิดขึ้นล่าสุด
แต่เนื่องจาก MySQL_Connection จะ throw MySQL_Exception ออกมาเมื่อมีความผิดพลาดเกิดขึ้น
และจะทำให้โปรแกรมจบการทำงานหากไม่ได้ดักจับ Exception
property ดังกล่าวจึงไม่อาจจะสามารถเข้าถึงได้ เพราะโปรแกรมจะจบการทำงานลงเสียก่อน
ดังนั้นหากจะตรวจจับ error เอง ต้องใช้ try ... catch
ดักจับความผิดพลาด ป้องกันการจบการทำงาน
try {
$mysql->query("SELECT * FROM order");
} catch (Exception $e) {
echo $mysql->errorMessage;
}
ซึ่งมีประโยชน์สำหรับการเขียน query ที่ปล่อยให้มีความผิดพลาดโดยตั้งใจ
เช่น INSERT ค่าที่อาจจะซ้ำกันลงในคอลัมน์ชนิด PRIMARY หรือ UNIQUE
INSERT id แบบสุ่ม หาก id ที่สุ่มได้มีอยู่แล้ว ก็จะ INSERT ไม่ได้ ให้ทำซ้ำจนกว่าจะได้ id ที่ไม่ซ้ำ
// สั่งให้ลูปไม่รู้จบ
while (true) {
try {
// id สุ่มตั้งแต่ 100000 - 999999
$mysql->query(
"
INSERT INTO `users`
(`id`, `username`, `password`)
VALUES
(100000 + ROUND(RAND() * 899999), %s, %s)
",
array($_POST['username'], md5($_POST['password']))
);
} catch (MySQL_Exception $exception) {
// หาก INSERT ไม่ได้ ก็จะมาตรงนี้
// ตรวจ mysql error code ว่าใช้ 1062 (Duplicate entry '???' for key ???) หรือไม่
if ($mysql->errorCode === 1062) {
// หากใช่ ก็สั่งให้ไปเริ่มต้นหลัง while (true) { อีกครั้ง
continue;
}
// แต่ถ้าไม่ใช่ แสดงว่าเป็น error อย่างอื่นที่ไม่คาดคิด
// ให้โยน exception ออกมาตามปกติเพื่อ debug
throw $exception;
}
// หาก INSERT ได้ ก็จะไม่มี exception จะมาตรงนี้
// สั่งให้ออกจากลูป
break;
}
ส่วน property อื่นๆ ที่ใช้บ่อยๆ ในการเขียนแบบเก่าเช่น mysql_affected_rows() หรือ mysql_insert_id()
นั้นไม่ได้รวมมาในนี้ครับ เพราะสามารถใช้ SQL แทนได้
ตัวแทน mysql_affected_rows()
// หากต้องการหาว่า มีกี่แถว ที่เปลี่ยนแปลงโดยคำสั่งประเภท UPDATE หรือ DELETE
// เราสามารถใช้ ROW_COUNT() ได้
$mysql->query("DELETE * FROM `table`");
$num_deleted_rows = $mysql->queryValue("SELECT ROW_COUNT()");
ตัวแทน mysql_insert_id()
// หากต้องการหาว่า id แบบ auto_increment ของแถวล่าสุดที่ INSERT ไป คืออะไร
// เราสามารถใช้ LAST_INSERT_ID() ได้
$mysql->query("INSERT INTO `table` VALUES ('Test')");
$id = $mysql->queryValue("SELECT LAST_INSERT_ID()");
ตัวแทน mysql_num_rows()
$result = $mysql->queryResult("SELECT * FROM `table`");
echo $result->numRows;
หรือ
$rows = $mysql->queryAndFetchAll("SELECT * FROM `table`");
echo count($rows);
บทความที่เกี่ยวข้อง
ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page
|
|
|
|
|
|
|
|
By : |
phpinfo()
|
|
Article : |
บทความเป็นการเขียนโดยสมาชิก หากมีปัญหาเรื่องลิขสิทธิ์ กรุณาแจ้งให้ทาง webmaster ทราบด้วยครับ |
|
Score Rating : |
|
|
Create Date : |
2013-06-02 |
|
Download : |
No files |
|
Sponsored Links |
|
|
|
|
|
|