|   | 
            
			 
   
     
        PHP อัพเดท MySQL_Connection เป็น Version 2 ครับ       | 
   
 
			
  อัพเดท MySQL_Connection เป็น Version 2 ครับ 
สำหรับผู้ที่เคยใช้ class MySQL_Connection ของผม วันนี้มีเวอร์ชั่นใหม่มาแนะนำครับ แต่ไม่ backward compatibility นะครับ ซึ่งเวอร์ชั่นนี้มีการเปลี่ยนแปลงและปรับปรุงในหลายจุดครับ 
 
1. เพิ่มความสามารถในการใช้ table prefix 
ซึ่งจะมีประโยชน์สำหรับคนที่ใช้โฮสต์ที่ไม่ให้สร้างฐานข้อมูลมากกว่า 1 หรือจำกัดจำนวนฐานข้อมูล จึงต้องแบ่งฐานข้อมูลตาม prefix ของตาราง 
 
รูปแบบการแทนที่คือ <ชื่อตาราง> 
 
$mysql->tablePrefix = 'web001_';
$mysql->query("SELECT * FROM <user>");
 
 
query ที่ได้จะเป็น 
 
SELECT * FROM `web001_user`
  
 
2. เพิ่มการ quote identifier ตามแบบ MS SQL Server 
คือใช้ [ชื่อคอลัมน์หรือตารางหรือฐานข้อมูล] เพราะ MySQL ใช้ตัวอักษร ` ในการ quote ซึ่งยากต่อการพิมพ์ด้วยคีย์บอร์ดภาษาไทย คนส่วนมากจึงมักไม่ใช้กันและทำให้เกิดปัญหาเช่น ตั้งชื่อคอลัมน์หรือตารางไปตรงกับคำสงวน (พบบ่อยที่สุดคือคำว่า ORDER) และทำให้ error จนต้องไปแก้ไขโครงสร้างตาราง ซึ่งเป็นการแก้ปัญหาที่ปลายเหตุ 
 
$mysql->query("SELECT * FROM [order] WHERE [from] = '[email protected]'");
 
 
query ที่ได้จะเป็น 
 
SELECT * FROM `order` WHERE `from` =  '[email protected]'
  
 
3. Auto Connect 
แค่สร้าง instance ของ class ขึ้นมา แล้วมันจะ connect ให้เองเมื่อเรียกใช้ method ใดๆ ที่จำเป็นต้องใช้ connection ในครั้งแรก 
 
$mysql = new MySQL_Connection('localhost', 'root', '', 'phpinfo');
// $mysql->connect();
...
...
...
// จะเริ่ม connect ตรงนี้
$value = $mysql->queryValue("SELECT 1");
 
 
และเพิ่ม arguments ในการเชื่อมต่อไปอีกสองตัว คือ port และ socket (ตาม mysqli_connect()) 
 
$mysql = new MySQL_Connection(
	'localhost',                 // Host
	'root',                      // Username
	'',                          // Password
	'phpinfo',                   // Default Database
	3307,                        // Port
	'/var/run/mysqld/mysql.sock' // Socket
);
  
 
4. ตัด class MySQL_Result และตัด method MySQL_Connection::queryResult() ออกไป 
และเพิ่ม MySQL_Connection::fetch() และ MySQL_Connection::fetchAll() เข้ามาแทน 
 
จากเดิมถ้าอยากจะค่อยๆ fetch result ออกมา 
 
$result = $mysql->query("SELECT * FROM `user`");
while ($row = $result->fetch()) {
    ...
}
$result->free();
 
 
ในเวอร์ชั่นนี้ใช้ Class MySQL เองเป็นตัว fetch 
 
$mysql->query("SELECT * FROM `user`");
while ($row = $mysql->fetch()) {
    ...
}
// run query ต่อไปได้เลยโดยไม่ต้องเรียก free() เพราะจะทำให้อัตโนมัติ
$mysql->query("SELECT * FROM `page`");
 
 
5. มี option debug ที่ถ้าเป็น false จะไม่จบการทำงานทันที 
แต่จะโยน MySQL_Exception ออกมาให้ดักจับ และเลือกจบการทำงานได้ด้วย MySQL_Exception::debug() 
 
$mysql->debug = false;
try {
	$mysql->query("SELECT * * FROM `user`");
} catch (MySQL_Exception $exception) {
	$exception->debug();
}
 
 
6. method MySQL_Connection::query() จะ return ค่าออกมาเป็นจำนวนแถวที่ SELECT ได้ หรือจำนวนแถวที่ UPDATE/DELETE ได้ 
 
if ($mysql->query("SELECT * FROM `user`")) {
    while ($row = $mysql->fetch()) {
        // ...
    }
    // กระทำการบางอย่างในกรณีที่มีข้อมูล ...
} else {
    // ไม่มีข้อมูล
}
 
 
  
 
Methods/Properties 
 
 
__construct([$host, [$username, [$password, [$db, [$port, [$socket]]]]]) 
 
สร้าง instance ของ MySQL_Connection 
$host - ชื่อหรือ IP address ของ MySQL Server เช่น 'localhost' หรือ '127.0.0.1' 
$username - ชื่อผู้ใช้ MySQL 
$password - รหัสผ่าน 
$db - default database สำหรับทุกๆ query สามารถเรียก selectDb() เพื่อเปลี่ยนในภายหลังได้ 
$port - หมายเลข TCP port ที่ใช้ในการเชื่อมต่อ 
$socket - Unix Domain Socket file ที่ใช้ในการเชื่อมต่อ 
 
$mysql = new MySQL_Connection('localhost', 'root', '');
 
 
 
 
connect([$host, [$username, [$password, [$db, [$port, [$socket]]]]]) 
 
ทำการเชื่อมต่อกับ MySQL Server 
แต่ method นี้ไม่จำเป็นต้องเรียกใช้ในกรณีทั่วไป เพราะการเรียก method อื่นๆ ที่ต้องการ connection จะทำการเรียก method นี้ให้อัตโนมัติ 
แต่จะมีประโยชน์ในกรณีที่ต้องการ connect ด้วย config ที่ต่างออกไปจากตอนสร้าง instance 
 
$mysql = new MySQL_Connection('localhost', 'root', '');
$mysql->connect('localhost', 'admin', '1234');
 
 
 
 
close() 
 
ตัดการเชื่อมต่อกับ MySQL Server เพื่อปล่อย connection ให้ process อื่นได้ใช้งาน 
ควรใช้หลังจากแน่ใจว่าจะไม่มีการใช้งาน database อีกแล้ว และ script อาจจะต้องทำงานต่ออีกสักระยะ 
เช่นการแสดงผลลัพธ์ด้วย template engine 
เพราะถ้าหากไม่เรียก close() การเชื่อมต่อจะยังคงอยู่จนกว่า script จะจบการทำงาน 
 
$mysql->close();
session_write_close();
$smarty->display('index.tpl');
 
 
 
 
selectDb($db) 
 
กำหนด default database สำหรับทุกๆ query 
 
$mysql->selectDb('phpinfo');
 
 
 
 
query($query [, $params]) 
 
ส่ง SQL Query ไปให้ MySQL Server ประมวลผล และคืนค่าแถวที่ SELECT/UPDATE/DELETE กลับมา 
 
echo $mysql->query("SELECT * FROM `user`");
 
 
method นี้มักจะใช้ร่วมกับ fetch() เพื่อค่อยๆ อ่านข้อมูลที่ SELECT ได้ออกมาทีละแถว 
 
if ($mysql->query("SELECT * FROM `user`") > 0) {
	while ($user = $mysql->fetch()) {
		// ...
	}
}
 
 
 
 
fetch([$columnKey]) 
 
อ่านค่าแถวหลังเรียกใช้ query() ออกมาครั้งละ 1 แถว 
ถ้าไม่เจอแถวใดใดเลยหรือยังไม่ได้เรียกใข้ query() หรือ query ที่เรียกไม่คืนค่าแถว เช่น UPDATE หรือ DELETE จะคืนค่ากลับมาเป็น null 
 
$mysql->query("SELECT * FROM `user`");
$first = $mysql->fetch(); // แถวที่ 1
$second = $mysql->fetch(); // แถวที่ 2
$third = $mysql->fetch(); // แถวที่ 3
 
 
 
 
fetchAll([$columnKey [, $indexKey]]) 
 
อ่านค่าแถวหลังเรียกใช้ query() ออกมาทั้งหมด 
method นี้คืนค่ากลับมาเป็น array เสมอ แม้จะไม่เจอแถวใดๆ เลยก็ตาม 
ดังนั้นจึงปลอดภัยที่จะเอาไปใช้กับ foreach หรือ function อื่นๆ ที่ต้องการ argument เป็น array 
 
$mysql->query("SELECT * FROM `user`");
$first = $mysql->fetch();
$second = $mysql->fetch();
$third = $mysql->fetch();
 
 
 
 
free() 
 
ทำการคืนหน่วยความจำที่ใช้ไปกับ result ที่ได้จากการ query ก่อนหน้า 
 
$mysql->query("SELECT * FROM `user`);
$user1 = $mysql->fetch();
$user2 = $mysql->fetch();
$mysql->free();
$user3 = $mysql->fetch(); // null
 
 
ซึ่งปกติเราไม่จำเป็นต้องเรียก method นี้ เพราะ method อื่นๆ เช่น queryAndFetch() หรือ queryAndFetchAll() จะทำการเรียกให้โดยอัตโนมัติ 
method นี้มีประโยชน์เฉพาะเวลาเรา SELECT ข้อมูลออกมาจำนวนหนึ่ง และต้องการ fetch() ทีละแถว และทำตัดสินใจเองว่าจะหยุดเมื่อไหร่ 
 
$mysql->query("SELECT * FROM `user`");
while ($user = $mysql->fetch()) {
	if ($user['type'] !== 'admin') {
		break;
	}
	$users[] = $user;
}
$mysql->free();
 
 
 
 
queryAndFetch($query [, $params, [$columnKey]]) 
 
ส่ง SQL Query ไปให้ MySQL Server ประมวลผล และคืนค่าแถวแรกที่เจอกลับมา 
ถ้าไม่เจอแถวใดใดเลยจะคืนค่ากลับมาเป็น null 
 
$user = $mysql->queryAndFetch(
	"SELECT * FROM `user` WHERE `username` = %s[username], `password` = MD5(%s[password]) LIMIT 1",
	$_POST
);
if (!isset($user)) {
	echo 'user not found or wrong password';
	exit;
}
 
 
method จะเรียกใช้ free() ทุกครั้ง ดังนั้นแม้จะมีข้อมูลที่ SELECT เจอมากกว่า 1 จะไม่สามารถดึงข้อมูลมากกว่านั้นได้อีก 
 
$user = $mysql->queryAndFetch("SELECT * FROM `user`");
$nextUser = $mysql->fetch(); // จะเป็น null เสมอ เพราะได้ free() ไปแล้ว
 
 
 
 
queryAndFetchAll($query [, $params, [$columnKey, [$indexKey]]]) 
 
ส่ง SQL Query ไปให้ MySQL Server ประมวลผล และคืนทุกแถวที่เจอกลับมา 
method นี้คืนค่ากลับมาเป็น array เสมอ แม้จะไม่เจอแถวใดๆ เลยก็ตาม 
ดังนั้นจึงปลอดภัยที่จะเอาไปใช้กับ foreach หรือ function อื่นๆ ที่ต้องการ argument เป็น array 
 
foreach ($mysql->queryAndFetchAll("SELECT * FROM `user`") as $user) {
	// ...
}
 
 
 
 
queryValue($query [, $params]) 
 
ส่ง SQL Query ไปให้ MySQL Server ประมวลผล และคืนฟิลด์แรกของแถวแรกที่เจอกลับมา 
มีประโยชน์สำหรับในการอ่านค่า query สั้นๆ เช่น SELECT COUNT(*) หรือ SELECT ROW_COUNT() 
 
$numUsers = $mysql->queryValue("SELECT COUNT(*) FROM `user`");
 
 
 
 
$insertId 
 
เป็น property ที่คืนค่า auto_increment ของแถวที่ INSERT ไปล่าสุด 
 
$mysql->query(
	"INSERT INTO `user` SET `username` = %s, `password` = MD5(%s)",
	array($_POST['username'], $_POST['password'])
);
echo $mysql->insertId;
  
 
หาก query ที่เรียกก่อนหน้าไม่ใช่ INSERT ก็จะ return 0 
 
 
 
$affectedRows 
 
เป็น property ที่คืนค่าแถวที่มีผลกระทบจากคำสั่งประเภท UPDATE/DELETE หรือจำนวนแถวที่ SELECT ได้ 
 
$mysql->query("DELETE FROM `user`");
echo $mysql->affectedRows;
 
 
ซึ่งปกติไม่จำเป็นต้องเรียกใช้ก็ได้ เพราะ query() คืนค่านี้กลับมาให้อยู่แล้ว 
 
echo $mysql->query("DELETE FROM `user`");
 
 
 
 
$numRows 
 
เป็น property ที่คืนจำนวนแถวที่ SELECT ได้ 
 
$mysql->query("SELECT * FROM `user`");
echo $mysql->numRows;
 
 
ซึ่งปกติไม่จำเป็นต้องเรียกใช้ก็ได้ เพราะ query() คืนค่านี้กลับมาให้อยู่แล้ว 
 
echo $mysql->query("SELECT * FROM `user`");
 
 
จะมีประโยชน์ก็ต่อเมื่อ query ด้วย method อื่น 
 
// แม้จะดึงออกมาแค่แถวแรก แต่จริงๆ MySQL อาจจะ SELECT เจอมากกว่า 1 แถว
$user = $mysql->queryAndFetch("SELECT * FROM `user`");
echo $mysql->query->numRows;
 
 
 
 
$tablePrefix 
 
prefix ของตารางที่จะเอาไปต่อหน้าการแทนที่ในรูปแบบ <ชื่อตาราง> 
 
$mysql->tablePrefix = 'web001_';
$mysql->query("SELECT * FROM <user>");
// SELECT * FROM `web001_user`
 
 
 
 
$charset 
 
default charset สำหรับการเชื่อมต่อ 
default คือ utf8 
 
$mysql->charset = 'tis620';
  
 
 
 
$debug 
 
หากเป็น true จะจบการทำงานทุกครั้งที่มี error เกิดขึ้นจากการ connect หรือ query 
แต่ถ้าเป็น false จะโยน MySQL_Exception ออกมาให้ดักจับ 
default เป็น true 
แต่ควรกำหนดเป็น false หากต้องการตรวจจับ error 
เช่นในกรณีที่ต้องการตรวจสอบว่ามีข้อมูลซ้ำหรือไม่ก่อน INSERT 
เราอาจจะไม่ต้อง SELECT เพื่อตรวจสอบ แต่แค่ INSERT ข้อมูลเข้าไป หากข้อมูลที่ INSERT มันมี UNIQUE KEY ซ้ำ ก็จะมี Exception โยนออกมา 
 
// ทำให้โยน Exception ออกมาหากมี error
$mysql->debug = false;
try {
	// หาก `username` มี index เป็น UNIQUE จะ INSERT ไม่ได้ถ้าข้อมูลที่เข้าไปใหม่ซ้ำกับของเดิม
	$mysql->query(
		"INSERT INTO `user` SET `username` = %s, `password` = MD5(%s)",
		array($_POST['username'], $_POST['password'])
	);
} catch (MySQL_Exception $exception) {
	// ... จัดการกับ error
}
 
 
 
 
$error 
$errno 
 
error message และ error code ของ error ที่เกิดขึ้นล่าสุด 
ซึ่ง property ทั้งสองนี้จะไม่มีประโยชน์หาก $debug เป็น true เพราะเมื่อเกิด error จะจบการทำงานก่อนได้อ่านค่าจาก property เหล่านี้ 
$mysql->debug = false;
try {
	// หาก `username` มี index เป็น UNIQUE จะ INSERT ไม่ได้ถ้าข้อมูลที่เข้าไปใหม่ซ้ำกับของเดิม
	$mysql->query(
		"INSERT INTO `user` SET `username` = %s, `password` = MD5(%s)",
		array($_POST['username'], $_POST['password'])
	);
} catch (MySQL_Exception $exception) {
	// ตรวจสอบ error code ว่าเป็นกรณี KEY ซ้ำหรือเปล่า
	// http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_dup_entry
	if ($mysql->errno === 1062 // ER_DUP_ENTRY
		|| $mysql->errno === 1586 // ER_DUP_ENTRY_WITH_KEY_NAME
	) {
		// ... แจ้งว่าข้อมูลซ้ำ
	} else {
		// error อื่นๆ
	}
}
 
 
 
  
 
อธิบาย Arguments ที่ใช้ใน query(), fetch(), fetchAll(), queryAndFetch(), queryAndFetchAll(), queryValue() 
 
 
$params - ค่าหรือ array ของค่าที่ต้องการแทนที่ใน query 
 
โดยการแทนที่นั้นจะใช้เครื่องหมาย % ตามด้วยอักษร i, s, b, d, f, % 
สามารถมี n ต่อท้าย (ยกเว้น %i และ %%) เพื่อกำหนดว่า ค่านี้สามารถเป็น NULL ใน SQL 
 
$mysql->query(
	"SELECT %s, %sn",
	array(null, null)
);
// SELECT '', NULL
  
 
และสามารถมี [ชื่อ key] ต่อท้ายเพื่อกำหนดว่าจะใช้ key ไหนใน array $params 
หากไม่กำหนด จะเรียงลำดับตั้งแต่ 0 ไปเรื่อยๆ 
 
$mysql->query(
	"SELECT %s, %s[abc], %sn[1000], %s, %s",
	array(
		'Test',
		'PHP',
		'MySQL',
		'abc' => 555,
		1000 => null,
	)
);
// SELECT 'Test', '555', NULL, 'PHP', 'MySQL'
  
 
สามารถลดการเขียนโค้ดลงได้ โดยส่ง array ที่มี key แน่ชัดอยู่แล้วเข้าไปเป็น $params โดยตรง 
 
// หาก $_POST มี key $_POST['username'] และ $_POST['password']
// ก็สามารถส่งไปเป็น $params ได้โดยตรง
$mysql->query(
	"
	INSERT INTO `user`
	(`username`, `password`)
	VALUES
	(%s[username], %s[password])
	"
	$_POST
);
  
 
หากค่าที่นำมาแทนที่เป็น array ก็จะแปลงให้เป็น list ของค่า (คั่นด้วย ,) 
เหมาะสำหรับเอาไปใช้ใน IN (...) หรือ INSERT INTO (...) VALUES (...) 
 
$superUsers = $mysql->queryAndFetchAll(
	"SELECT * FROM `user` WHERE `type` IN (%s)",
	// ต้องเป็น array ซ้อน array มิเช่นนั้นจะถูกมองว่าเป็นค่าเดี่ยวๆ
	// เช่น หากใช้ array('admin', 'vip') แบบนี้ %s จะถูกแทนที่ด้วย 'admin'
	array(
		array('admin', 'vip')
	)
);
// SELECT * FROM `user` WHERE `type` IN ('admin','vip')
 
 
$mysql->query(
	"INSERT INTO `user` (%i) VALUES (%s)",
	array(
		array('username', 'password'), // %i list ของ identifier
		array('phpinfo', '1234'), // %s list ของ string
	)
);
// INSERT INTO `user` (`username`, `password`) VALUES ('phpinfo', '1234')
 
 
  
 
i - แปลงค่าให้เป็น identifier คือครอบด้วย ` 
 
$mysql->query("SELECT * FROM %i", 'user');
// SELECT * FROM `user`
 
 
ถ้าในค่าที่เอาไปแทนที่มีเครื่องหมาย . ก็จะทำการแยกส่วนให้ 
 
$mysql->query("SELECT * FROM %i", 'phpinfo.user');
// SELECT * FROM `phpinfo`.`user`
 
 
s - แปลงค่าให้เป็น SQL string ซึ่งจะทำการ escape ค่าให้โดยอัตโนมัติ เพื่อป้องกัน SQL Injection 
 
$mysql->query("SELECT %s", 123);
// SELECT '123'
$mysql->query("SELECT %s", "That's it!!!");
// SELECT 'That\'s it!!!'
 
 
d - แปลงค่าให้เป็นเลขจำนวนเต็ม (int) 
 
$mysql->query("SELECT %d", '123.123');
// SELECT 123
 
 
f - แปลงค่าให้เป็นเลขจำนวนจริง (float) 
 
$mysql->query("SELECT %f", '123.123');
// SELECT 123.123
 
 
b - แปลงค่าให้เป็น SQL boolean คือ TRUE หรือ FALSE 
 
$mysql->query("SELECT %b", 123);
// SELECT TRUE
$mysql->query("SELECT %b", 0);
// SELECT FALSE
 
 
% - เอาค่าที่ส่งไปแทนที่ตรงๆ โดยไม่ escape ใดใดทั้งสิ้น ควรใช้อย่างระมัดระวัง 
 
$mysql->query("SELECT %%", "COUNT(*)");
// SELECT COUNT(*)
 
 
หากในค่าที่ส่งไปมีสัญลักษณ์การแทนที่อื่นๆ ก็จะทำการแทนที่นั้นแบบ recursive 
 
$mysql->query(
	"SELECT %%",
	array("%d[x] * %d[y]", 'x' => 2, 'y' => 4)
);
// SELECT 2 * 4
 
 
เหมาะสำหรับเพิ่มเงื่อนไขแบบ dynamic ให้กับ query 
 
// ถ้ามี $_GET['username'] ส่งมา
if (isset($_GET['username'])) {
	// เพิ่มเงื่อนไข
	$where[] = `username` = %s[username]";
}
// ถ้ามี $_GET['email'] ส่งมา
if (isset($_GET['email'])) {
	// เพิ่มเงื่อนไข
	$where[] = "`email` = %s[email]";
}
$_GET[0] = isset($where)
	? 'WHERE ' . implode(' OR ', $where)
	: '';
$mysql->query("SELECT * FROM `user` %%", $_GET);
/*
สมมติว่าถ้า $_GET['username'] = 'phpinfo' ก็จะได้ query
SELECT * FROM `user` WHERE `username` = 'phpinfo'
แต่ถ้าไม่มีตามเงื่อนไขที่กำหนดก็จะได้ query
SELECT * FROM `user`
*/
 
 
 
 
$columnKey - ดึงเฉพาะค่าของฟิลด์ที่ต้องการออกมา 
 
โดยปกติการ fetch จะดึงข้อมูลออกมาเป็น array ข้อมูลของแถว แม้ในแถวนั้นๆ จะมีแค่ฟิลด์เดียวก็ตาม 
 
$user = $mysql->queryAndFetchAll("SELECT `username` FROM `user`");
/*
array(
	array('username' => 'phpinfo'),
	array('username' => 'test'),
)
*/
 
 
แต่ถ้าเราอยากได้ array เฉพาะค่าของฟิลด์ที่ต้องการ 
 
$users = $mysql->queryAndFetchAll("SELECT `username` FROM `user`", null, 'username');
/*
array(
	'phpinfo',
	'test',
)
*/
 
 
 
 
$indexKey - กำหนดให้ค่าของฟิลด์ที่ต้องการเป็น key ของ array 
 
โดยปกติการ fetchAll จะดึงข้อมูลออกมาเป็น array ข้อมูลของแถวโดยเรียงลำดับจาก 0 ไปเรื่อยๆ 
แต่ถ้าเราอยากให้ array ที่ได้ มี key จากค่าในแถว เราสามาถใช้ $indexKey ช่วยได้ 
 
$users = $mysql->queryAndFetchAll("SELECT `username`, `email` FROM `user`", null, null, 'username');
/*
array(
	'phpinfo' => array('email' => '[email protected]'),
	'test' => array('email' => '[email protected]'),
)
*/
 
 
ใช้ร่วมกับ $columnKey 
 
$users = $mysql->queryAndFetchAll("SELECT `username`, `email` FROM `user`", null, 'email', 'username');
/*
array(
	'phpinfo' => '[email protected]',
	'test' => '[email protected]',
)
*/
 
 
 
Source Code 
<?php
/**
* MySQL_Connection V2
* คลาสที่จะช่วยให้คุณเขียนโปรแกรมเชื่อมต่อกับฐาน MySQL ได้สะดวกและปลอดภัยขึ้น
* Copyright (c) 2014, phpinfo.in.th (http://www.phpinfo.in.th)
*/
class MySQL_Connection
{
	protected static $options = array(
		'mysqli.default_host',
		'mysqli.default_user',
		'mysqli.default_pw',
		null,
		'mysqli.default_port',
		'mysqli.default_socket',
	);
	private $_debug = true;
	private $_tablePrefix = '';
	private $_defaultCharset = 'utf8';
	private $_affectedRows;
	private $_numRows;
	private $args;
	private $c;
	private $r;
	private $s = array();
	private $d;
	private $i;
	public function __construct(
		$host = null,
		$username = null,
		$password = null,
		$db = null,
		$port = null,
		$socket = null
	) {
		$args = array($host, $username, $password, $db, $port, $socket);
		foreach (self::$options as $i => $default) {
			if (!isset($args[$i]) && isset($default)) {
				$args[$i] = ini_get($default);
			}
		}
		$this->args = $args;
	}
	protected function replaceStringCallback($matches)
	{
		if (isset($matches[3])) {
			$nullable = isset($matches[3][1]);
			if (isset($matches[4])) {
				$value = isset($this->d[$matches[4]])
					? $this->d[$matches[4]]
					: null;
			} else {
				$value = isset($this->d[$this->i])
					? $this->d[$this->i++]
					: null;
			}
			if (is_array($value)) {
				switch ($matches[3][0]) {
					case '%':
						return $this->replaceString(implode(',', $value));
					case 'i':
						foreach ($value as &$ref) {
							$ref = implode(
								'`.`',
								explode('.', preg_replace('/`/u', '``', $ref), 3)
							);
						}
						return '`' . implode('`,`', $value) . '`';
					case 'b':
						foreach ($value as &$ref) {
							$ref = $nullable && $ref === null
								? 'NULL'
								: ((bool) $ref ? 'TRUE' : 'FALSE');
						}
						return implode(',', $value);
					case 'd':
						foreach ($value as &$ref) {
							$ref = $nullable && $ref === null
								? 'NULL'
								: (int)$ref;
						}
						return implode(', ', $value);
					case 'f':
						foreach ($value as &$ref) {
							$ref = $nullable && $ref === null
								? 'NULL'
								: (float)$ref;
						}
						return implode(', ', $value);
					case 's':
						foreach ($value as &$ref) {
							$ref = $nullable && $ref === null
								? 'NULL'
								: $this->c->real_escape_string($ref);
						}
						return "'" . implode("','", $value) . "'";
				}
			} else {
				switch ($matches[3][0]) {
					case '%':
						return $this->replaceString($value);
					case 'i':
						return '`'
							. implode(
								'`.`',
								explode('.', preg_replace('/`/u', '``', $value), 3)
							)
							. '`';
					case 'b':
						return $nullable && $value === null
							? 'NULL'
							: ((bool) $value ? 'TRUE' : 'FALSE');
					case 'd':
						return $nullable && $value === null
							? 'NULL'
							: (int)$value;
					case 'f':
						return $nullable && $value === null
							? 'NULL'
							: (float)$value;
					case 's':
						return $nullable && $value === null
							? 'NULL'
							: "'{$this->c->real_escape_string($value)}'";
				}
			}
		} elseif (isset($matches[2])) {
			return "`{$this->_tablePrefix}{$matches[2]}`";
		} elseif (isset($matches[1])) {
			return "`{$matches[1]}`";
		}
		return $matches[0];
	}
	public function connect()
	{
		if (isset($this->c)) {
			return;
		}
		$this->c = mysqli_init();
		$args = func_get_args();
		@call_user_func_array(array($this->c, 'connect'), $args + $this->args);
		if ($this->c->connect_error) {
			$exception = new MySQL_Exception(
				$this->c->connect_error,
				$this->c->connect_errno
			);
			$this->c = null;
			if ($this->_debug) {
				$exception->debug();
			}
			throw $exception;
		}
		$this->c->set_charset($this->_defaultCharset);
	}
	public function selectDb($db)
	{
		$this->connect();
		$this->c->select_db($db);
		if ($this->c->error) {
			$exception = new MySQL_Exception($this->c->error, $this->c->errno);
			if ($this->_debug) {
				$exception->debug();
			}
			throw $exception;
		}
	}
	public function ping()
	{
		$this->connect();
		return $this->c->ping();
	}
	public function close()
	{
		if (!isset($this->c)) {
			return;
		}
		$this->free();
		$return = $this->c->close();
		$this->c = null;
		return $return;
	}
	public function escapeString($value)
	{
		if (!isset($this->c)) {
			$this->connect();
		}
		return $this->c->real_escape_string($value);
	}
	public function replaceString()
	{
		if (!isset($this->c)) {
			$this->connect();
		}
		$args = func_get_args();
		$query = array_shift($args);
		if ($args) {
			if (!isset($args[1])) {
				$args = is_array($args[0]) ? $args[0] : array($args[0]);
			}
			if (isset($this->d)) {
				$this->s[] = array($this->d, $this->i);
			}
			$this->d = $args;
			$this->i = 0;
		}
		$result = preg_replace_callback(
			'/\'(?>\\\\\'|\'\'|[^\']+)*\'|"(?>\\\\"|""|[^"]+)*"|`(?>``|[^`]+)*`|\[([^\s\]]+)\]|\<([^\s\>]+)\>|%(%|i|(?>s|b|d|f)n?)?(?>\[([^\]]+)\])?|%/u',
			array($this, 'replaceStringCallback'),
			$query
		);
		if ($args) {
			if (!empty($this->s)) {
				list($this->d, $this->i) = array_pop($this->s);
			} else {
				$this->d = null;
			}
		}
		return $result;
	}
	public function query($query, $params = null)
	{
		$this->connect();
		$this->free();
		$result = $this->c->query($actualQuery = $this->replaceString($query, $params));
		if ($result === false) {
			$exception = new MySQL_Exception(
				$this->c->error,
				$this->c->errno,
				$query,
				$actualQuery
			);
			if ($this->_debug) {
				$exception->debug();
			}
		}
		if ($result instanceof mysqli_result) {
			$this->r = $result;
			return $this->_numRows = $this->_affectedRows = $this->c->affected_rows;
		}
		$this->_numRows = 0;
		return $this->_affectedRows = $this->c->affected_rows;
	}
	public function queryAndFetch($query, $params = null, $columnKey = null)
	{
		$this->query($query, $params);
		$row = $this->fetch($columnKey);
		$this->free();
		return $row;
	}
	public function queryAndFetchAll($query, $params = null, $columnKey = null, $indexKey = null)
	{
		$this->query($query, $params);
		return $this->fetchAll($columnKey, $indexKey);
	}
	public function queryValue($query, $params = null)
	{
		if ($this->query($query, $params)) {
			$row = $this->fetch();
			$this->free();
			return $row[key($row)];
		}
	}
	public function fetch($columnKey = null)
	{
		if (!isset($this->r)) {
			return;
		}
		if (!($row = $this->r->fetch_assoc())) {
			$this->free();
		} else {
			return isset($columnKey)
				? (isset($row[$columnKey])
					? $row[$columnKey]
					: null)
				: $row;
		}
	}
	public function fetchAll($columnKey = null, $indexKey = null)
	{
		if (!isset($this->r)) {
			return;
		}
		$rows = array();
		if (($row = $this->r->fetch_assoc())) {
			if (isset($columnKey)) {
				if (array_key_exists($columnKey, $row)) {
					if (isset($indexKey) && array_key_exists($indexKey, $row)) {
						do {
							$rows[$row[$indexKey]] = $row[$columnKey];
						} while (($row = $this->r->fetch_assoc()));
					} else {
						do {
							$rows[] = $row[$columnKey];
						} while (($row = $this->r->fetch_assoc()));
					}
				}
			} elseif (isset($indexKey) && array_key_exists($indexKey, $row)) {
				do {
					$rows[$row[$indexKey]] = $row;
				} while (($row = $this->r->fetch_assoc()));
			} else {
				do {
					$rows[] = $row;
				} while (($row = $this->r->fetch_assoc()));
			}
		}
		$this->free();
		return $rows;
	}
	public function free()
	{
		if (!isset($this->r)) {
			return;
		}
		$this->r->free();
		$this->r = null;
	}
	public function __get($name)
	{
		if (isset($this->{$prop = '_' . $name})) {
			return $this->$prop;
		}
		if ($name === 'charset') {
			$this->connect();
			return $this->c->character_set_name();
		}
		static $map = array(
			'error' => 'error',
			'errno' => 'errno',
			'insertId' => 'insert_id',
		);
		if (isset($map[$name])) {
			return $this->c->{$map[$name]};
		}
	}
	public function __set($name, $value)
	{
		if (isset($this->{$prop = '_' . $name})) {
			$this->$prop = $name === 'debug'
				? (bool)$value
				: (string)$value;
		}
		if ($name === 'charset') {
			$this->connect();
			$this->c->set_charset($value);
			if ($this->c->error) {
				$exception = new MySQL_Exception($this->c->error, $this->c->errno);
				if ($this->_debug) {
					$exception->debug();
				}
				throw $exception;
			}
		}
	}
}
class MySQL_Exception extends Exception
{
	public static function formatMessage($message, $width = 100)
	{
		$width = max(40, (int)$width);
		if (!preg_match_all('/^\t+/mu', $message, $matches)) {
			$matches[0][0] = '';
		}
		sort($matches[0]);
		$message = trim(
			preg_replace(
				'/\t/u',
				'    ',
				preg_replace("/^{$matches[0][0]}/mu", '', $message)
			)
		);
		if (preg_match_all("/\\n?(.{0,{$width}})/u", $message, $matches, PREG_SET_ORDER)) {
			foreach ($matches as $match) {
				$lines[] = $match[1];
			}
			return implode("\n", $lines);
		}
		return $message;
	}
	private $sourceQuery;
	private $actualQuery;
	public function __construct($message, $code, $sourceQuery = null, $actualQuery = null)
	{
		parent::__construct((string)$message, (int)$code);
		$this->sourceQuery = $sourceQuery;
		$this->actualQuery = $actualQuery;
	}
	public function getDebugMessage($width = 100)
	{
		$hr = str_repeat('-', $width = max(40, (int)$width));
		return self::formatMessage(
			isset($this->sourceQuery)
				? sprintf(
					"{$hr}\nMySQL error:\n{$hr}\n\n#%s - %s\n\n{$hr}\nSource query:\n{$hr}\n\n%s\n\n{$hr}\nActual query:\n{$hr}\n\n%s\n\n{$hr}\nTrace:\n{$hr}\n\n%s",
					$this->getCode(),
					$this->getMessage(),
					$this->sourceQuery,
					$this->actualQuery,
					$this->getTraceAsString()
				)
				: sprintf(
					"{$hr}\nMySQL error:\n{$hr}\n\n#%s - %s\n\n{$hr}\nTrace:\n{$hr}\n\n%s",
					$this->getCode(),
					$this->getMessage(),
					$this->getTraceAsString()
				),
			$width
		);
	}
	public function debug($width = 100)
	{
		if (!headers_sent()) {
			header('Content-Type: text/plain; charset=utf-8', true);
			while (ob_get_level()) {
				ob_end_clean();
			}
		}
		exit($this->getDebugMessage($width));
	}
}
      
		          
                 
    
                             					
			
              			             
                           
                             |   | 
                               | 
                             	
		                        | 
                            
                           
                             | 
 | 
                               | 
                              
                             			
 |