ข้อความ/เรียบเรียงโดย Zhu Xianzhong
1. บทนำ
โชคดีที่มีการนำเทคโนโลยีการโอเวอร์โหลดวัตถุมาใช้ใน PHP 5.0 บทความนี้จะสำรวจความเป็นไปได้ของการโอเวอร์โหลดเมธอด __call(), __set() และ __get() หลังจากการแนะนำสั้น ๆ เกี่ยวกับทฤษฎีการโอเวอร์โหลด เราจะตรงไปที่หัวข้อผ่านสองตัวอย่าง: ตัวอย่างแรกคือการใช้คลาสหน่วยเก็บข้อมูลถาวร ตัวอย่างที่สองคือการค้นหาวิธีการนำไดนามิก getter/setter ไปใช้งาน
2. การโอเวอร์โหลดวัตถุคืออะไร?
เมื่อพูดถึง object overloading ใน PHP เราต้องแยกแยะสองประเภท:
·Method overloading
·Attribute overloading
ในกรณีของ method overloading เราต้องกำหนดเมธอด __call() ซึ่งจะใช้การเรียกทั่วไปแบบ A ไปยังเมธอดที่ไม่ได้กำหนด ในชั้นเรียนที่เกี่ยวข้อง วิธีการทั่วไปนี้เรียกว่าเฉพาะเมื่อคุณต้องการเข้าถึงวิธีการที่ไม่ได้กำหนดในชั้นเรียน หากไม่มีเมธอดโอเวอร์โหลด ตัวอย่างต่อไปนี้จะทำให้ PHP แสดงข้อความแสดงข้อผิดพลาดร้ายแรง: การเรียกไปยังเมธอดที่ไม่ได้กำหนด ThisWillFail::bar() ใน/some/directory/example.php ออนไลน์ที่บรรทัด 9 และยกเลิกการทำงานของโปรแกรม:
< ?php
คลาส ThisWillFail {
ฟังก์ชั่นสาธารณะ foo() {
กลับ "สวัสดีชาวโลก!";
-
-
$class = ใหม่ ThisWillFail;
$คลาส->บาร์();
?>
ด้วยความช่วยเหลือของวิธีการโอเวอร์โหลด โค้ดสามารถรับสายนี้และจัดการได้อย่างสง่างาม
การโอเวอร์โหลดคุณสมบัติจะคล้ายกับการโอเวอร์โหลดของวิธีการ ในกรณีนี้ การดำเนินการอ่าน/เขียนคลาส (หรือที่เรียกว่าพรอกซี) ไปยังคุณสมบัติของคลาสที่ไม่ได้กำหนดไว้อย่างชัดเจนในคลาส วิธีการพิเศษที่นี่คือ __set() และ __get() ขึ้นอยู่กับระดับการรายงานข้อผิดพลาด นักแปล PHP มักจะออกการแจ้งเตือนเมื่อเข้าถึงคุณสมบัติที่ไม่ได้กำหนด หรือเลื่อนและอาจกำหนดตัวแปร หากคุณใช้แอตทริบิวต์โอเวอร์โหลด นักแปลสามารถเรียก __set() เมื่อตั้งค่าแอตทริบิวต์ที่ไม่ได้กำหนด และเรียก __get() เมื่อเข้าถึงค่าแอตทริบิวต์ที่ไม่ได้กำหนด
โดยสรุป การใช้เทคโนโลยีโอเวอร์โหลดสามารถลดระยะเวลาในการพัฒนาซอฟต์แวร์ได้อย่างมากเมื่อใช้ภาษาไดนามิก เช่น PHP
ณ จุดนี้ ทฤษฎีจะถูกนำเสนอ และการวิเคราะห์โค้ดเฉพาะเจาะจงด้านล่าง
3. ตัวอย่างของคลาสการจัดเก็บข้อมูลถาวร รหัส
ต่อไปนี้ใช้คลาสการจัดเก็บข้อมูลถาวรที่กล่าวถึงข้างต้นด้วยโค้ด PHP น้อยกว่า 50 บรรทัดโดยใช้เทคโนโลยีการโอเวอร์โหลดแอตทริบิวต์ คำว่าคงอยู่หมายความว่าคลาสสามารถอธิบายองค์ประกอบจากโครงสร้างข้อมูลและยังคงซิงโครไนซ์กับระบบจัดเก็บข้อมูลพื้นฐาน ในแง่การเขียนโค้ด โค้ดภายนอกสามารถใช้คลาสเพื่อเลือกแถวจากตารางฐานข้อมูลได้ ด้วยวิธีนี้ เมื่อโปรแกรมกำลังทำงาน คุณจะสามารถเข้าถึงคุณลักษณะของคลาสได้โดยตรงเพื่อจัดการองค์ประกอบในแถว (อ่าน/ดึงข้อมูล) ในตอนท้ายของสคริปต์ PHP จะรับผิดชอบในการส่งข้อมูลแถวที่อัปเดตกลับไปยังฐานข้อมูล
การศึกษาโค้ดต่อไปนี้อย่างละเอียดจะช่วยให้คุณเข้าใจว่าแอตทริบิวต์ที่โอเวอร์โหลดคืออะไร
<?php
//โหลด <a href=" http://pear.php.net/package/DB/ "> แพ็คเกจ DB ของ PEAR </a>
need_once "DB.php";
คลาสที่คงอยู่ได้ {
ส่วนตัว $data = array();
ส่วนตัว $table = "ผู้ใช้";
ฟังก์ชั่นสาธารณะ __ สร้าง ($ ผู้ใช้) {
$นี่->dbh = DB::Connect("mysql://user:password@localhost/database");
$query = "เลือก id, ชื่อ, อีเมล, ประเทศจาก"
$this->table . " WHERE name = ?";
$this->data = $this->dbh->getRow($query, array($user)
DB_FETCHMODE_ASSOC);
-
ฟังก์ชั่นสาธารณะ __get ($ สมาชิก) {
ถ้า (isset($this->data[$member])) {
ส่งคืน $this->data[$member];
-
-
ฟังก์ชั่นสาธารณะ __set($สมาชิก, $value) {
//ID ของชุดข้อมูลเป็นแบบอ่านอย่างเดียวถ้า ($member == "id") {
กลับ;
-
ถ้า (isset($this->data[$member])) {
$this->data[$member] = $value;
-
-
ฟังก์ชั่นสาธารณะ __destruct() {
$query = "UPDATE" . $this->table " ชื่อเซ็ต = ?,
อีเมล = ?, ประเทศ = ? โดยที่ id = ?";
$this->dbh->query($query, $this->ชื่อ, $this->อีเมล,
$นี่->ประเทศ, $นี่->id);
-
-
$class = ใหม่ Persistable("Martin Jansen");
$class->name = "จอห์น โด";
$class->country = "สหรัฐอเมริกา";
$class->email = " [email protected] ";
?
ปัญหาแรกที่คุณอาจพบคือ __construct() ซึ่งเป็นเมธอด Constructor ใหม่ที่นำมาใช้ใน PHP 5 ในสมัยของ PHP 4 ตัวสร้างจะจับคู่ชื่อคลาสของตนเสมอ นี่ไม่ใช่กรณีใน PHP 5 อีกต่อไป คุณไม่จำเป็นต้องรู้มากเกี่ยวกับเมธอด Constructor ยกเว้นว่าการเรียกมันจะสร้างอินสแตนซ์ของคลาส และสังเกตว่ามีการใช้พารามิเตอร์ที่นี่ - ฐานข้อมูลจะถูกดำเนินการตามพารามิเตอร์นี้ ตัวสร้างนี้กำหนดผลลัพธ์การสืบค้นให้กับแอตทริบิวต์คลาส $data
ต่อไป โปรแกรมจะกำหนดเมธอดพิเศษ 2 เมธอด __get() และ __set() คุณควรจะคุ้นเคยกับสิ่งเหล่านี้แล้ว: __get() ใช้เพื่ออ่านค่าแอตทริบิวต์ที่ไม่ได้กำหนด และ __set() ใช้เพื่อแก้ไขค่าแอตทริบิวต์ที่ไม่ได้กำหนด
ซึ่งหมายความว่าเมื่อใดก็ตามที่คุณสมบัติที่ไม่ได้กำหนดถูกอ่าน/เขียนจากคลาสหน่วยเก็บข้อมูลถาวร วิธีการพิเศษเหล่านี้จะรับผิดชอบในการจัดการข้อมูลในตัวแปรอาร์เรย์คุณสมบัติ $data แทนที่จะเปลี่ยนคุณสมบัติของคลาสโดยตรง (โปรดจำไว้ว่า: ตัวแปร $data มีแถวจากฐานข้อมูล!)
เมธอดสุดท้ายในคลาสนั้นตรงกันข้ามกับ __construct() - destructor __destruct() PHP เรียก destructor ในระหว่าง "ระยะการปิดระบบสคริปต์" ซึ่งโดยปกติแล้วจะใกล้สิ้นสุดการทำงานของสคริปต์ PHP destructor เขียนข้อมูลจากแอตทริบิวต์ $data กลับไปยังฐานข้อมูล นี่คือความหมายที่แท้จริงของการซิงโครไนซ์คำก่อนหน้านี้
คุณอาจสังเกตเห็นว่าโค้ดที่นี่ใช้แพ็คเกจ abstraction layer ฐานข้อมูลของ PEAR จริงๆ แล้ว มันไม่สำคัญเลย การสื่อสารกับฐานข้อมูลผ่านวิธีการอื่นๆ ก็สามารถอธิบายหัวข้อของบทความนี้ได้เช่นกัน
หากคุณพิจารณาอย่างรอบคอบ คุณจะพบว่าคำอธิบายของคลาสหน่วยเก็บข้อมูลถาวรนี้ค่อนข้างง่าย ตัวอย่างเกี่ยวข้องกับตารางฐานข้อมูลเท่านั้น และไม่พิจารณาโมเดลข้อมูลที่ซับซ้อนมากขึ้น เช่น การใช้ LEFT JOIN และเทคนิคการดำเนินการฐานข้อมูลที่ซับซ้อนอื่นๆ อย่างไรก็ตาม คุณไม่จำเป็นต้องผูกมัดกับสิ่งนี้ และด้วยความช่วยเหลือจากคุณสมบัติที่โอเวอร์โหลด คุณจึงสามารถใช้โมเดลฐานข้อมูลในอุดมคติของคุณเองได้ ด้วยโค้ดเพียงเล็กน้อย คุณสามารถใช้ประโยชน์จากคุณลักษณะฐานข้อมูลที่ซับซ้อนในคลาสพื้นที่จัดเก็บข้อมูลถาวรนี้ได้
นอกจากนี้ยังมีปัญหาเล็กน้อย - ไม่มีการแนะนำกลไกการจัดการข้อผิดพลาดเมื่อแบบสอบถามล้มเหลวใน destructor มันเป็นธรรมชาติของตัวทำลายที่ทำให้ไม่สามารถแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสมในกรณีนี้ได้ เนื่องจากการสร้างมาร์กอัป HTML มักจะจบลงก่อนที่ PHP จะเรียกตัวทำลาย
เพื่อแก้ไขปัญหานี้ คุณสามารถเปลี่ยนชื่อ __destruct() เป็นชื่ออื่น เช่น saveData() และดำเนินการวิธีนี้ด้วยตนเองที่ใดที่หนึ่งในสคริปต์การโทร สิ่งนี้ไม่ได้เปลี่ยนแนวคิดของการจัดเก็บข้อมูลแบบถาวรสำหรับคลาส มันเป็นเพียงโค้ดเพิ่มเติมสองสามบรรทัด หรือคุณสามารถใช้ฟังก์ชัน error_log() ใน destructor เพื่อบันทึกข้อความแสดงข้อผิดพลาดลงในไฟล์บันทึกข้อผิดพลาดทั้งระบบ
นี่คือการทำงานของคุณสมบัติที่โอเวอร์โหลด ต่อไปเราจะพูดถึงวิธีการโอเวอร์โหลด
4. ตัวอย่างของเมธอดโอเวอร์โหลด
1. เมธอด Dynamic Getter/Setter
โค้ดต่อไปนี้ใช้เมธอด getter/setter "ไดนามิก" เพื่อควบคุมคลาสด้วยความช่วยเหลือของเมธอดโอเวอร์โหลด มาวิเคราะห์ตามซอร์สโค้ด:
<?php
คลาส DynamicGetterSetter {
$name ส่วนตัว = "Martin Jansen";
$starbucksdrink ส่วนตัว = "คาราเมลคาปูชิโน่ Swirl";
ฟังก์ชั่น __call($method, $arguments) {
$คำนำหน้า = strtolower(substr($method, 0, 3));
$property = strtolower(substr($method, 3));
ถ้า (ว่างเปล่า($คำนำหน้า) || ว่างเปล่า($คุณสมบัติ)) {
กลับ;
-
ถ้า ($คำนำหน้า == "รับ" && isset($this->$property)) {
ส่งคืน $this->$property;
-
ถ้า ($ คำนำหน้า == "ตั้งค่า") {
$this->$property = $อาร์กิวเมนต์[0];
-
-
-
$คลาส = DynamicGetterSetter ใหม่;
echo "ชื่อ: " . $class->getName() .
echo "รสชาติสตาร์บัคส์ที่ชอบ: " . $class->getStarbucksDrink() .
$class->setName("จอห์น โด");
$class->setStarbucksDrink("กาแฟคลาสสิค");
echo "ชื่อ: " . $class->getName() .
echo "รสชาติสตาร์บัคส์ที่ชอบ: " . $class->getStarbucksDrink() .
?>
แน่นอนว่าแอตทริบิวต์ทั้งสอง $name และ $starbucksdrink ที่นี่เป็นแบบส่วนตัว ซึ่งหมายความว่าไม่สามารถเข้าถึงคุณลักษณะเหล่านี้จากภายนอกคลาสได้ ในการเขียนโปรแกรมเชิงวัตถุ เป็นเรื่องปกติมากที่จะใช้วิธี public getter/setter เพื่อเข้าถึงหรือแก้ไขค่าของคุณสมบัติที่ไม่ใช่แบบสาธารณะ การดำเนินการเหล่านี้เป็นเรื่องที่น่าเบื่อและสิ้นเปลืองเวลาและความพยายาม
ปัญหานี้สามารถแก้ไขได้ง่ายด้วยความช่วยเหลือของวิธีการโอเวอร์โหลด แทนที่จะใช้เมธอด getter/setter สำหรับแต่ละคุณสมบัติ ข้างต้นจะใช้เมธอด __call() ทั่วไปเท่านั้น ซึ่งหมายความว่าเมื่อมีการเรียกใช้เมธอด getter/setter ที่ไม่ได้กำหนดไว้ เช่น setName() หรือ getStarbucksdrink() PHP จะไม่สร้างข้อผิดพลาดร้ายแรงและยกเลิก แต่จะดำเนินการ (หรือมอบหมายให้) เมธอด __call() อันมหัศจรรย์แทน
ต่อไปนี้เป็นการแนะนำสั้น ๆ เรามาทำการวิเคราะห์เชิงลึกของ __call() กัน
2. การวิเคราะห์โดยละเอียดของวิธีการ __call()
พารามิเตอร์แรกของ __call() เป็นวิธีการดั้งเดิมและไม่ได้กำหนดไว้ (เช่น setName) พารามิเตอร์ตัวที่สองคืออาร์เรย์หนึ่งมิติที่มีดัชนีตัวเลขซึ่งมีวิธีการดั้งเดิมทั้งหมด . พารามิเตอร์ การเรียกเมธอดที่ไม่ได้กำหนดด้วยพารามิเตอร์สองตัว ("Martin" และ 42) จะสร้างอาร์เรย์ต่อไปนี้:
$class->thisMethodDoesNotExist("Martin", 42);
/แนะนำพารามิเตอร์ตัวที่สองของ __call()
อาร์เรย์
-
[0] =>มาร์ติน
[1] => 42
)
ภายในเมธอด __call() หากเมธอดเดิมเริ่มต้นด้วย get หรือ set จะต้องคำนวณบางอย่างเพื่อพิจารณาว่าโค้ดนั้นเรียกใช้เมธอด getter/setter หรือไม่ นอกจากนี้ เมธอดนี้จะวิเคราะห์ส่วนประกอบอื่นของชื่อเมธอดเพิ่มเติม (ยกเว้นอักขระสามตัวแรก) เนื่องจากส่วนหลังของสตริงแสดงถึงชื่อของแอ็ตทริบิวต์ที่อ้างอิงโดย getter/setter
หากชื่อเมธอดบ่งชี้ getter/setter ดังนั้นเมธอดจะส่งคืนค่าคุณสมบัติที่สอดคล้องกันหรือตั้งค่าของพารามิเตอร์แรกของเมธอดดั้งเดิม ถ้าไม่เช่นนั้น จะไม่ทำอะไรเลยและรันโปรแกรมต่อไปเหมือนกับไม่มีอะไรเกิดขึ้น
เพื่อให้บรรลุเป้าหมาย
โดยพื้นฐานแล้ว มีวิธีหนึ่งที่อนุญาตให้โค้ดเรียกใช้เมธอด getter/setter แบบไดนามิกได้ วิธีนี้จะสะดวกเมื่อพัฒนาต้นแบบโปรแกรมในระยะสั้น: แทนที่จะใช้เวลามากมายไปกับการติดตั้ง getters/setters นักพัฒนาสามารถมุ่งเน้นไปที่การสร้างแบบจำลอง API และสร้างความมั่นใจว่าแอปพลิเคชันนั้นถูกต้องโดยพื้นฐาน การรวมเมธอด __call() เข้ากับคลาสนามธรรมอาจทำให้คุณใช้โค้ดซ้ำในการพัฒนาโปรเจ็กต์ PHP ในอนาคตได้
4. นอกจากข้อบกพร่องแล้ว
ยังมีข้อดีและข้อเสีย
อีกด้วยวิธีการข้างต้นมีข้อเสียหลายประการ: โปรเจ็กต์ขนาดใหญ่อาจใช้เครื่องมือ เช่น phpDocumentor เพื่อติดตามโครงสร้าง API ด้วยวิธีการไดนามิกที่แนะนำข้างต้น แน่นอนว่าวิธีการ getter/setter ทั้งหมดจะไม่ปรากฏในเอกสารที่สร้างขึ้นโดยอัตโนมัติ ซึ่งไม่ต้องการคำอธิบายเพิ่มเติม
ข้อเสียเปรียบอีกประการหนึ่งคือโค้ดที่อยู่นอกคลาสสามารถเข้าถึงทรัพย์สินส่วนตัวทั้งหมดภายในคลาสได้ เมื่อใช้วิธีการ getter/setter จริง คุณสามารถแยกความแตกต่างระหว่างคุณสมบัติส่วนตัวที่สามารถเข้าถึงได้ด้วยโค้ดภายนอก และคุณสมบัติส่วนตัว "ของจริง" ที่ไม่สามารถมองเห็นได้นอกคลาส - เนื่องจากเรามีเมธอดที่โอเวอร์โหลด และเรามี getters และ setters เสมือน สามารถใช้วิธีการได้
บทความนี้จะวิเคราะห์สองสถานการณ์ของการโอเวอร์โหลดอ็อบเจ็กต์ใน PHP 5.0 อย่างรอบคอบผ่านสอง
ตัวอย่าง
ฉันหวังเป็นอย่างยิ่งว่าวิธีการในบทความนี้จะช่วยคุณปรับปรุงประสิทธิภาพของการเขียนโปรแกรม PHP ในขณะเดียวกันคุณควรเห็นข้อบกพร่องของวิธีนี้อย่างชัดเจน