1. วัตถุ DBQuery
ตอนนี้ วัตถุ DBQuery ของเราจำลองขั้นตอนการจัดเก็บ - เมื่อดำเนินการแล้วจะส่งคืนทรัพยากรผลลัพธ์ที่ต้องบันทึกไว้ และหากคุณต้องการใช้ฟังก์ชันกับชุดผลลัพธ์ (เช่น num_rows() หรือ fetch_row() ) ) คุณต้องส่งผ่านวัตถุ MySqlDB ดังนั้นจะเกิดอะไรขึ้นถ้าวัตถุ DBQuery ใช้งานฟังก์ชั่นที่นำมาใช้โดยวัตถุ MySqlDB (ซึ่งออกแบบมาเพื่อทำงานกับผลลัพธ์ของแบบสอบถามที่ดำเนินการ)? ลองใช้โค้ดจากตัวอย่างก่อนหน้านี้ต่อไป และสมมติว่าทรัพยากรผลลัพธ์ของเราได้รับการจัดการโดยออบเจ็กต์ DBQuery ซอร์สโค้ดของคลาส DBQuery แสดงอยู่ในรายการ 1
รายการ 1. การใช้คลาส DBQuery
ต้องการ 'mysql_db.php';
need_once 'query.php';
$db = MySqlDb ใหม่;
$db->connect('host', 'username', 'pass');
$db->query('ใช้ content_management_system');
$query = DBQuery ใหม่($db);
$query->prepare('SELECT fname,sname FROM users WHERE username=:1S AND pword=:2S AND expir_time<:3I');
พยายาม {
if($query->execute("visualad", "apron", time()))->num_rows() == 1) {
echo('ข้อมูลประจำตัวที่ถูกต้อง');
} อื่น {
echo('ข้อมูลประจำตัวไม่ถูกต้อง / เซสชันหมดอายุ');
-
} จับ (QueryException $e) {
echo('เกิดข้อผิดพลาดในการสืบค้น: ' . $e);
}
สิ่งที่เราสนใจมากที่สุดในโค้ดที่แก้ไขด้านบนคือคำสั่ง catch และคำสั่งดำเนินการ
· คำสั่งดำเนินการจะไม่ส่งคืนทรัพยากรผลลัพธ์อีกต่อไป แต่ตอนนี้จะส่งคืนออบเจ็กต์ DBQuery เอง
· ขณะนี้อ็อบเจ็กต์ DBQuery ใช้ฟังก์ชัน num_rows() ซึ่งเราคุ้นเคยอยู่แล้วจากอินเทอร์เฟซ DB
· หากการดำเนินการสืบค้นล้มเหลว ก็จะส่งข้อยกเว้นประเภท QueryException เมื่อแปลงเป็นสตริง จะส่งกลับรายละเอียดของข้อผิดพลาดที่เกิดขึ้น
ในการดำเนินการนี้ คุณต้องใช้พรอกซี ที่จริงแล้ว คุณกำลังใช้พรอกซีในออบเจ็กต์ DBQuery ของเราอยู่แล้ว แต่ตอนนี้ คุณจะใช้พรอกซีในเชิงลึกมากขึ้นเพื่อผูกมันเข้ากับออบเจ็กต์ MySqlDB อย่างแน่นหนา ออบเจ็กต์ DBQuery ได้รับการเตรียมใช้งานด้วยออบเจ็กต์ที่ใช้อินเทอร์เฟซ DB และมีฟังก์ชันสมาชิกที่ดำเนินการอยู่แล้ว ซึ่งเรียกเมธอด query() ของออบเจ็กต์ DB เพื่อดำเนินการค้นหา วัตถุ DBQuery เองไม่ได้สืบค้นฐานข้อมูลจริง ๆ แต่จะปล่อยให้งานนี้อยู่กับวัตถุ DB นี่คือพร็อกซีซึ่งเป็นกระบวนการที่ออบเจ็กต์สามารถนำพฤติกรรมเฉพาะไปใช้โดยการส่งข้อความไปยังออบเจ็กต์อื่นที่ใช้พฤติกรรมแบบเดียวกันหรือคล้ายกัน
ในการดำเนินการนี้ คุณจะต้องแก้ไขออบเจ็กต์ DBQuery เพื่อรวมฟังก์ชันทั้งหมดที่ทำงานบนทรัพยากรผลลัพธ์จากออบเจ็กต์ DB คุณจำเป็นต้องใช้ผลลัพธ์ที่เก็บไว้เมื่อดำเนินการค้นหาเพื่อเรียกใช้ฟังก์ชันที่เกี่ยวข้องของออบเจ็กต์ DB และส่งคืนผลลัพธ์ ฟังก์ชั่นต่อไปนี้จะถูกเพิ่ม:
รายการที่ 2: การขยายคลาส DBQuery โดยใช้พรอกซี
classDBQuery
-
.....
ฟังก์ชั่นสาธารณะ fetch_array()
-
ถ้า (! is_resource($this->ผลลัพธ์)) {
โยนข้อยกเว้นใหม่ ('ไม่ได้ดำเนินการค้นหา');
-
ส่งคืน $this->db->fetch_array($this->ผลลัพธ์);
}
ฟังก์ชั่นสาธารณะ fetch_row()
-
ถ้า (! is_resource($this->ผลลัพธ์)) {
โยนข้อยกเว้นใหม่ ('ไม่ได้ดำเนินการค้นหา');
-
ส่งคืน $this->db->fetch_row($this->ผลลัพธ์);
}
ฟังก์ชั่นสาธารณะ fetch_assoc()
-
ถ้า (! is_resource($this->ผลลัพธ์)) {
โยนข้อยกเว้นใหม่ ('ไม่ได้ดำเนินการค้นหา');
-
ส่งคืน $this->db->fetch_assoc($this->ผลลัพธ์);
}
ฟังก์ชั่นสาธารณะ fetch_object()
-
ถ้า (! is_resource($this->ผลลัพธ์)) {
โยนข้อยกเว้นใหม่ ('ไม่ได้ดำเนินการค้นหา');
-
ส่งคืน $this->db->fetch_object($this->ผลลัพธ์);
}
ฟังก์ชั่นสาธารณะ num_rows()
-
ถ้า (! is_resource($this->ผลลัพธ์)) {
โยนข้อยกเว้นใหม่ ('ไม่ได้ดำเนินการค้นหา');
-
ส่งคืน $this->db->num_rows($this->ผลลัพธ์);
-
}
การใช้งานแต่ละฟังก์ชันนั้นค่อนข้างง่าย ขั้นแรกจะตรวจสอบเพื่อให้แน่ใจว่าแบบสอบถามได้รับการดำเนินการแล้ว จากนั้นมอบหมายงานให้กับอ็อบเจ็กต์ DB โดยส่งคืนผลลัพธ์ราวกับว่าเป็นอ็อบเจ็กต์คิวรีนั้นเอง (เรียกว่าฟังก์ชันฐานข้อมูลพื้นฐาน)
2. พิมพ์ Hinting
เพื่อให้พร็อกซีทำงานได้ เราต้องแน่ใจว่าตัวแปร $db ของออบเจ็กต์ DBQuery เป็นอินสแตนซ์ของออบเจ็กต์ที่ใช้อินเทอร์เฟซ DB คำแนะนำประเภทเป็นคุณลักษณะใหม่ใน PHP 5 ที่ช่วยให้คุณสามารถบังคับพารามิเตอร์ฟังก์ชันลงในวัตถุประเภทเฉพาะได้ ก่อน PHP 5 วิธีเดียวที่จะให้แน่ใจว่าพารามิเตอร์ฟังก์ชันเป็นประเภทอ็อบเจ็กต์เฉพาะคือการใช้ฟังก์ชันตรวจสอบประเภทที่มีให้ใน PHP (นั่นคือ is_a()) ตอนนี้คุณสามารถแคสต์ประเภทออบเจ็กต์ได้โดยใส่ชื่อประเภทนำหน้าพารามิเตอร์ฟังก์ชัน คุณได้เห็นคำแนะนำประเภทจากออบเจ็กต์ DBQuery ของเราแล้ว ซึ่งช่วยให้มั่นใจว่าออบเจ็กต์ที่ใช้อินเทอร์เฟซ DB จะถูกส่งผ่านไปยังตัวสร้างออบเจ็กต์
ฟังก์ชั่นสาธารณะ __ สร้าง (DB $db)
-
$นี่->db = $db;
}
เมื่อใช้คำแนะนำประเภท คุณสามารถระบุได้ไม่เพียงแต่ประเภทออบเจ็กต์เท่านั้น แต่ยังรวมถึงคลาสนามธรรมและอินเทอร์เฟซด้วย
3. โยนข้อยกเว้น
คุณอาจสังเกตเห็นจากโค้ดข้างต้นว่าสิ่งที่คุณจับได้นั้นเป็นข้อยกเว้นที่เรียกว่า QueryException (เราจะใช้วัตถุนี้ในภายหลัง) ข้อยกเว้นคล้ายกับข้อผิดพลาด แต่จะกว้างกว่า วิธีที่ดีที่สุดในการอธิบายข้อยกเว้นคือการใช้กรณีฉุกเฉิน แม้ว่าเหตุฉุกเฉินอาจไม่ "ร้ายแรง" แต่ก็ยังต้องได้รับการจัดการ เมื่อมีข้อยกเว้นเกิดขึ้นใน PHP ขอบเขตการดำเนินการปัจจุบันจะสิ้นสุดลงอย่างรวดเร็ว ไม่ว่าจะเป็นฟังก์ชัน try..catch block หรือตัวสคริปต์เอง จากนั้นข้อยกเว้นจะข้ามผ่าน Call Stack โดยยุติแต่ละขอบเขตการดำเนินการ จนกว่าจะถูกจับได้ในบล็อก try..catch หรือไปถึงจุดสูงสุดของ Call Stack ซึ่ง ณ จุดนี้จะสร้างข้อผิดพลาดร้ายแรง
การจัดการข้อยกเว้นเป็นอีกคุณสมบัติใหม่ใน PHP 5 เมื่อใช้ร่วมกับ OOP จะสามารถควบคุมการจัดการและการรายงานข้อผิดพลาดได้ดี บล็อก try..catch เป็นกลไกสำคัญในการจัดการกับข้อยกเว้น เมื่อตรวจพบแล้ว การเรียกใช้สคริปต์จะดำเนินต่อไปจากบรรทัดถัดไปของโค้ดซึ่งตรวจพบและจัดการข้อยกเว้น
หากการสืบค้นล้มเหลว คุณต้องเปลี่ยนฟังก์ชันดำเนินการเพื่อส่งข้อยกเว้น คุณจะโยนวัตถุข้อยกเว้นแบบกำหนดเองที่เรียกว่า QueryException - วัตถุ DBQuery ที่ทำให้เกิดข้อผิดพลาดจะถูกส่งผ่านไป
รายการ 3 โยนข้อยกเว้น
-
*ดำเนินการค้นหาปัจจุบัน
-
* ดำเนินการค้นหาปัจจุบัน โดยแทนที่จุดด้วยอาร์กิวเมนต์ที่ให้มา
-
-
* @parameters: ผสม $queryParams,... พารามิเตอร์การสืบค้น
* @return: ทรัพยากร A—ข้อมูลอ้างอิงที่อธิบายทรัพยากรที่ใช้ดำเนินการค้นหา
-
ฟังก์ชั่นสาธารณะดำเนินการ ($queryParams = '')
-
//ตัวอย่าง: SELECT * FROM table WHERE name=:1S AND type=:2I AND level=:3N
$args = func_get_args();
ถ้า ($ นี้ -> stored_procedure) {
/*เรียกใช้ฟังก์ชันคอมไพล์เพื่อรับแบบสอบถาม*/
$query = call_user_func_array(array($this, 'compile'), $args);
} อื่น {
/*ยังไม่ได้เตรียมใช้งาน Stored Procedure ดังนั้นจึงดำเนินการเป็นแบบสอบถามมาตรฐาน*/
$query = $queryParams;
-
$result = $this->db->query($query);
ถ้า (! $ ผลลัพธ์) {
โยน QueryException ใหม่ ($ นี้);
-
$นี่->ผลลัพธ์ = $ผลลัพธ์;
/* สังเกตว่าตอนนี้เราคืนอ็อบเจ็กต์ได้อย่างไร ซึ่งช่วยให้เราสามารถเรียกใช้ฟังก์ชันสมาชิกจากผลลัพธ์ที่ส่งคืนของฟังก์ชันนี้ */
ส่งคืน $ นี้;
}
4. ใช้การสืบทอดเพื่อส่งข้อยกเว้นแบบกำหนดเอง
ใน PHP คุณสามารถส่งอ็อบเจ็กต์ใดก็ได้เป็นข้อยกเว้น อย่างไรก็ตาม อันดับแรกข้อยกเว้นควรสืบทอดจากคลาสข้อยกเว้นในตัวของ PHP ด้วยการสร้างข้อยกเว้นที่คุณกำหนดเอง คุณสามารถบันทึกข้อมูลอื่นๆ เกี่ยวกับข้อผิดพลาด สร้างรายการในไฟล์บันทึก หรือทำทุกอย่างที่คุณต้องการ ข้อยกเว้นแบบกำหนดเองของคุณจะดำเนินการดังต่อไปนี้:
· บันทึกข้อความแสดงข้อผิดพลาดจากอ็อบเจ็กต์ DB ที่สร้างโดยการสืบค้น
· ให้รายละเอียดที่ชัดเจนของบรรทัดโค้ดที่เกิดข้อผิดพลาดในการสืบค้น โดยตรวจสอบ call stack
· แสดงข้อความแสดงข้อผิดพลาดและข้อความค้นหา—เมื่อแปลงเป็นสตริง
ในการรับข้อความแสดงข้อผิดพลาดและข้อความค้นหา จำเป็นต้องทำการเปลี่ยนแปลงหลายอย่างกับออบเจ็กต์ DBQuery
1. จำเป็นต้องเพิ่มแอตทริบิวต์ที่ได้รับการป้องกันใหม่—compiledQuery—ในชั้นเรียน
2. ฟังก์ชัน Compile() จะอัปเดตคุณสมบัติ CompiledQuery ของคิวรีด้วยข้อความคิวรี
3. ควรเพิ่มฟังก์ชันเพื่อดึงข้อความค้นหาที่คอมไพล์แล้ว
4. ควรเพิ่มฟังก์ชันด้วย - รับวัตถุ DB ปัจจุบันที่เกี่ยวข้องกับวัตถุ DBQuery
รายการ 4 โยนข้อยกเว้น
classDBQuery
-
-
*เก็บเวอร์ชันที่คอมไพล์ของเคียวรีหลังจากเรียกคอมไพล์() หรือเอ็กซีคิวต์()*
* @var string $compiledQuery
-
ป้องกัน $compiledQuery;
-
* ส่งคืนแบบสอบถามที่คอมไพล์แล้วโดยไม่ต้องดำเนินการ
* @parameters: ผสม $params,...พารามิเตอร์แบบสอบถาม* @return: string—แบบสอบถามที่คอมไพล์แล้ว*/
คอมไพล์ฟังก์ชันสาธารณะ($params='')
-
ถ้า (! $this->stored_procedure) {
Throw new Exception("ขั้นตอนการจัดเก็บยังไม่ได้รับการเตรียมใช้งาน");
-
/*การแทนที่พารามิเตอร์*/
$params = func_get_args(); //รับพารามิเตอร์ของฟังก์ชัน $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
return ($this->compiledQuery = $this->add_strings($query)); //ใส่สตริงกลับเข้าไปในแบบสอบถาม}
ฟังก์ชั่นสาธารณะ getDB()
-
ส่งกลับ $this->db;
-
ฟังก์ชั่นสาธารณะ getCompiledQuery()
-
กลับ $this->compiledQuery;
-
}
ตอนนี้คุณสามารถใช้คลาส QueryException ได้แล้ว สังเกตว่าคุณเดินผ่าน call stack เพื่อค้นหาตำแหน่งในสคริปต์ที่ทำให้เกิดข้อผิดพลาดได้อย่างไร นี่เป็นกรณีที่วัตถุ DBQuery ที่ส่งข้อยกเว้นเป็นคลาสย่อยที่สืบทอดมาจากวัตถุ DBQuery
รายการ 5: คลาส QueryException
-
*ข้อยกเว้นแบบสอบถาม
-
*เมื่อพยายามดำเนินการค้นหา หากมีข้อผิดพลาดเกิดขึ้น ข้อผิดพลาดจะเกิดขึ้นโดยออบเจ็กต์ {@link DBQuery}
-
คลาส QueryException ขยายข้อยกเว้น
-
-
*ข้อความสอบถาม*
* @var สตริง $QueryText;
-
ป้องกัน $QueryText;
-
*หมายเลข/รหัสผิดพลาดจากฐานข้อมูล*
* @var string $ErrorCode
-
ป้องกัน $ErrorNumber;
-
*ข้อความแสดงข้อผิดพลาดจากฐานข้อมูล*
* @var string $ErrorMessage
-
ป้องกัน $ErrorMessage;
-
*ตัวสร้างคลาส*
* @Parameter: DBQuery $db ซึ่งเป็นวัตถุแบบสอบถามที่ส่งข้อยกเว้น */
ฟังก์ชั่นสาธารณะ __ สร้าง (DBQuery $ แบบสอบถาม)
-
/*รับ call stack*/
$backtrace = $this->GetTrace();
/*กำหนดบรรทัดและไฟล์ไปยังตำแหน่งที่เกิดข้อผิดพลาดจริง*/
ถ้า (นับ ($backtrace) > 0) {
$x = 1;
/*หากคลาสเคียวรีสืบทอดมา เราจะต้องละเว้นการเรียกของคลาสย่อย*/
ในขณะที่((! isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !== false ) {
/*การดำเนินการวนซ้ำ ตราบใดที่ไม่มีหมายเลขบรรทัดหรือฟังก์ชันที่เรียกว่าเป็นคลาสย่อยของคลาส DBQuery*/
++$x;
/*ถ้าเราไปถึงจุดต่ำสุดของสแต็ก เราจะใช้ผู้เรียกคนแรก*/
ถ้า (($x) >= นับ($backtrace)) {
$x = จำนวน($ย้อนรอย);
หยุดพัก;
-
-
/*หากการวนซ้ำด้านบนดำเนินการอย่างน้อยหนึ่งครั้ง เราสามารถลดลงได้ 1 เพื่อค้นหาบรรทัดโค้ดจริงที่ทำให้เกิดข้อผิดพลาด*/
ถ้า ($x != 1) {
$x -= 1;
-
/*สุดท้ายเราก็สามารถตั้งค่าไฟล์และหมายเลขบรรทัดได้ ซึ่งควรสะท้อนถึงคำสั่ง SQL ที่ทำให้เกิดข้อผิดพลาด*/
$this->line = $backtrace[$x]['line'];
$this->file = $backtrace[$x]['file'];
-
$this->QueryText = $query->getCompiledQuery();
$this->ErrorNumber = $query->getDB()->errno();
$this->ErrorMessage = $query->getDB()->error();
/*เรียกตัวสร้างข้อยกเว้นของซูเปอร์คลาส*/
parent::__construct('ข้อผิดพลาดในการสืบค้น', 0);
-
-
*รับข้อความสอบถาม*
* ข้อความค้นหาสตริง @return */
ฟังก์ชั่นสาธารณะ GetQueryText()
-
กลับ $this->QueryText;
-
-
*ได้รับหมายเลขผิดพลาด*
* หมายเลขข้อผิดพลาดสตริง @return */
ฟังก์ชั่นสาธารณะ GetErrorNumber()
-
ส่งคืน $this->ErrorNumber;
-
-
*ได้รับข้อความแสดงข้อผิดพลาด*
* ข้อความแสดงข้อผิดพลาดสตริง @return */
ฟังก์ชั่นสาธารณะ GetErrorMessage()
-
กลับ $this->ErrorMessage;
-
-
* เรียกว่าเมื่อวัตถุถูกแปลงเป็นสตริง
* @return สตริง */
ฟังก์ชั่นสาธารณะ __toString()
-
$output = "แบบสอบถามผิดพลาดใน {$this->file} ออนไลน์ {$this->line}nn";
$output .= "แบบสอบถาม: {$this->QueryText}n";
$output .= "ข้อผิดพลาด: {$this->ErrorMessage} ({$this->ErrorNumber})nn"
;
-
}
ตอนนี้โค้ดที่คุณเห็นตอนต้นของส่วนนี้ใช้งานได้แล้ว
5. บทสรุป
ในบทความนี้ คุณได้เห็นวิธีที่ตัวแทนแมปอินเทอร์เฟซ DB ที่เชื่อมโยงกับการสืบค้นกับการดำเนินการกับผลลัพธ์การสืบค้นเฉพาะ ออบเจ็กต์ DBQuery เปิดเผยฟังก์ชันเดียวกัน เช่น fetch_assoc() เป็นออบเจ็กต์ DB อย่างไรก็ตาม ทั้งหมดนี้ใช้ได้กับแบบสอบถามเดียว คุณยังได้เรียนรู้วิธีใช้ข้อยกเว้นแบบกำหนดเองเพื่อให้ข้อมูลโดยละเอียดว่าเกิดข้อผิดพลาดเมื่อใดและที่ไหน และจะควบคุมการจัดการข้อผิดพลาดได้ดียิ่งขึ้นได้อย่างไร