มาถึงที่นี่ 2 ปีหลังจากถามคำถามเดิมมีบางสิ่งที่ฉันต้องการชี้ให้เห็น (อย่าขอให้ฉันชี้ให้เห็นสิ่งต่างๆมากมาย )
ตะขอที่เหมาะสม
หากต้องการสร้างคลาสปลั๊กอินให้ใช้ตะขอที่เหมาะสม ไม่มีกฎทั่วไปที่เป็นเพราะมันขึ้นอยู่กับสิ่งที่ชั้นเรียนทำ
การใช้เบ็ดแรก ๆ ที่"plugins_loaded"
มักจะไม่สมเหตุสมผลเพราะตะขอแบบนั้นถูกใช้สำหรับการร้องขอของผู้ดูแลระบบ frontend และ AJAX แต่บ่อยครั้งตะขอในภายหลังนั้นจะดีกว่ามาก
เช่นคลาสที่ทำสิ่งของสำหรับแม่แบบสามารถสร้างอินสแตนซ์"template_redirect"
ได้
โดยทั่วไปการพูดเป็นเรื่องยากมากที่คลาสจะต้องมีอินสแตนซ์ก่อนที่จะ"wp_loaded"
ถูกไล่ออก
ไม่มีคลาสของพระเจ้า
คลาสทั้งหมดส่วนใหญ่ที่ใช้เป็นตัวอย่างในคำตอบที่เก่ากว่าใช้คลาสที่ชื่อว่า like "Prefix_Example_Plugin"
หรือ"My_Plugin"
... สิ่งนี้บ่งชี้ว่าอาจมีคลาสหลักสำหรับปลั๊กอิน
ดีเว้นแต่ปลั๊กอินจะทำโดยชั้นเดียว (ในกรณีที่ตั้งชื่อมันหลังจากชื่อปลั๊กอินมีความเหมาะสมอย่างยิ่ง) ในการสร้างชั้นเรียนที่จัดการปลั๊กอินทั้งหมด (เช่นการเพิ่มตะขอทั้งหมดที่ปลั๊กอินต้องการหรือสร้างคลาสปลั๊กอินอื่นทั้งหมดทันที ) ถือได้ว่ามีการปฏิบัติที่ไม่ดีเป็นตัวอย่างของหนึ่งวัตถุพระเจ้า
ในเชิงวัตถุรหัสโปรแกรมควรจะมีแนวโน้มที่จะแข็งที่ "S" ยืน"หลักการความรับผิดชอบโสด"
หมายความว่าทุกชั้นควรทำอย่างเดียว ในการพัฒนาปลั๊กอิน WordPress หมายความว่าผู้พัฒนาควรหลีกเลี่ยงการใช้ hook เดี่ยวเพื่อยกตัวอย่างคลาสปลั๊กอินหลักแต่ควรใช้ hooks ที่แตกต่างกันเพื่อยกตัวอย่างคลาสที่แตกต่างกันตามความรับผิดชอบของคลาส
หลีกเลี่ยง hooks ในตัวสร้าง
อาร์กิวเมนต์นี้ได้รับการแนะนำในคำตอบอื่น ๆ ที่นี่ แต่ฉันต้องการที่จะสังเกตเห็นแนวคิดนี้และเชื่อมโยงคำตอบอื่น ๆที่มันได้รับการอธิบายอย่างกว้างขวางในขอบเขตของการทดสอบหน่วย
เกือบ 2015: PHP 5.2 สำหรับซอมบี้
ตั้งแต่ 14 สิงหาคม 2014, PHP 5.3 ถึงจุดสิ้นสุดของชีวิต แน่นอนมันตายแล้ว PHP 5.4 จะได้รับการสนับสนุนตลอดปี 2558 ซึ่งหมายความว่าอีกหนึ่งปีในขณะที่ฉันกำลังเขียน
อย่างไรก็ตาม WordPress ยังคงรองรับ PHP 5.2 แต่ไม่มีใครควรเขียนโค้ดบรรทัดเดียวที่รองรับเวอร์ชันนั้นโดยเฉพาะถ้าโค้ดเป็น OOP
มีเหตุผลแตกต่างกัน:
- PHP 5.2 ตายไปนานแล้วไม่มีการแก้ไขความปลอดภัยใด ๆ ซึ่งหมายความว่าไม่ปลอดภัย
- PHP 5.3 เพิ่มคุณสมบัติมากมายไปยัง PHP, ฟังก์ชั่นที่ไม่ระบุชื่อและเนมสเปซ über alles
- รุ่นใหม่ของ PHP เป็นจำนวนมากได้เร็วขึ้น PHP ฟรี อัปเดตมันฟรี เหตุใดจึงต้องใช้รุ่นที่ช้ากว่าและไม่ปลอดภัยหากคุณสามารถใช้รุ่นที่เร็วกว่าและปลอดภัยกว่าได้ฟรี
หากคุณไม่ต้องการใช้รหัส PHP 5.4+ ให้ใช้อย่างน้อย 5.3+
ตัวอย่าง
ณ จุดนี้ถึงเวลาที่จะทบทวนคำตอบที่เก่ากว่าตามสิ่งที่ฉันพูดจนถึงที่นี่
เมื่อเราไม่ต้องสนใจ 5.2 อีกต่อไปเราสามารถและควรใช้เนมสเปซ
เพื่ออธิบายหลักการความรับผิดชอบเดี่ยวของฉันได้ดีกว่าตัวอย่างของฉันจะใช้ 3 ชั้นเรียนหนึ่งที่ทำอะไรบางอย่างในส่วนหน้าส่วนที่ด้านหลังและที่สามใช้ในทั้งสองกรณี
ชั้นผู้ดูแลระบบ:
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
ส่วนหน้าระดับ:
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
เครื่องมืออินเตอร์เฟซ:
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
และคลาสเครื่องมือที่ใช้โดยอีกสองรายการ:
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
มีคลาสนี้ฉันสามารถยกตัวอย่างพวกเขาโดยใช้ตะขอที่เหมาะสม สิ่งที่ต้องการ:
require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';
add_action( 'admin_init', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $admin_stuff; // this is not ideal, reason is explained below
$admin_stuff = new GM\WPSE\Example\AdminStuff( $tools );
} );
add_action( 'template_redirect', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $front_stuff; // this is not ideal, reason is explained below
$front_stuff = new GM\WPSE\Example\FrontStuff( $tools );
} );
การพึ่งพาการผกผันและการพึ่งพาการฉีด
ในตัวอย่างด้านบนฉันใช้เนมสเปซและฟังก์ชั่นที่ไม่ระบุชื่อเพื่อยกตัวอย่างคลาสที่แตกต่างกันใน hooks ที่ต่างกันวางในสิ่งที่ฉันพูดไว้ข้างต้น
โปรดสังเกตว่าเนมสเปซอนุญาตให้สร้างคลาสที่ตั้งชื่อโดยไม่มีคำนำหน้าใด ๆ
ฉันใช้แนวคิดอื่นที่ถูกกล่าวถึงโดยอ้อมข้างต้น: Dependency Injectionมันเป็นวิธีหนึ่งในการใช้Dependency Inversion Principle "D" ในตัวย่อของ SOLID
Tools
ระดับคือ "ฉีด" ในอีกสองชั้นเมื่อพวกเขาจะ instantiated ดังนั้นในวิธีนี้ก็เป็นไปได้ที่จะแยกความรับผิดชอบ
นอกจากนี้AdminStuff
และFrontStuff
เรียนใช้ประเภทเค้าToolsInterface
จะประกาศที่พวกเขาต้องการคลาสที่นำไปปฏิบัติ
ด้วยวิธีนี้เองหรือผู้ใช้ที่ใช้รหัสของเราอาจใช้การใช้งานที่แตกต่างกันของอินเทอร์เฟซเดียวกันทำให้รหัสของเราไม่ได้ควบคู่กับคลาสที่เป็นรูปธรรม แต่เป็นนามธรรม: นั่นคือสิ่งที่หลักการพึ่งพาการพึ่งพา
อย่างไรก็ตามตัวอย่างข้างต้นสามารถปรับปรุงเพิ่มเติมได้ เรามาดูกันว่า
autoloader
วิธีที่ดีในการเขียนโค้ด OOP ที่อ่านได้ดีกว่าคือไม่ต้องผสมนิยาม (การเชื่อมต่อคลาส) เข้ากับรหัสอื่นและใส่ทุกประเภทไว้ในไฟล์ของตัวเอง
กฎข้อนี้ยังเป็นหนึ่งในPSR-1 มาตรฐานการเข้ารหัส 1
อย่างไรก็ตามการทำเช่นนั้นก่อนที่จะสามารถใช้คลาสหนึ่งจำเป็นต้องมีไฟล์ที่มีมัน
นี่อาจเป็นเรื่องยาก แต่ PHP มีฟังก์ชั่นยูทิลิตี้เพื่อโหลดคลาสโดยอัตโนมัติเมื่อจำเป็นโดยใช้การเรียกกลับที่โหลดไฟล์ตามชื่อ
การใช้เนมสเปซมันง่ายมากเพราะตอนนี้มันเป็นไปได้ที่จะจับคู่โครงสร้างโฟลเดอร์กับโครงสร้างเนมสเปซ
ไม่เพียงเป็นไปได้ แต่ยังเป็นอีกมาตรฐาน PSR (หรือดีกว่า 2: PSR-0เลิกใช้แล้วและPSR-4 )
การปฏิบัติตามมาตรฐานนั้นเป็นไปได้ที่จะใช้ประโยชน์จากเครื่องมือต่างๆที่จัดการ autoload โดยไม่ต้องเขียนรหัส autoloader ที่กำหนดเอง
ฉันต้องบอกว่ามาตรฐานการเข้ารหัสของ WordPressมีกฎแตกต่างกันสำหรับการตั้งชื่อไฟล์
ดังนั้นเมื่อเขียนโค้ดสำหรับ WordPress หลักนักพัฒนาจะต้องปฏิบัติตามกฎ WP แต่เมื่อเขียนรหัสที่กำหนดเองมันเป็นทางเลือกที่นักพัฒนา แต่ใช้มาตรฐาน PSR จะง่ายต่อการใช้เครื่องมือเขียนแล้ว2
รูปแบบการเข้าถึงทั่วโลกการลงทะเบียนและการบริการ
หนึ่งในปัญหาที่ใหญ่ที่สุดเมื่อสร้างอินสแตนซ์ปลั๊กอินใน WordPress เป็นวิธีการเข้าถึงจากส่วนต่าง ๆ ของรหัส
WordPress ใช้วิธีการทั่วโลก : ตัวแปรถูกบันทึกในขอบเขตทั่วโลกทำให้สามารถเข้าถึงได้ทุกที่ นักพัฒนา WP ทุกคนพิมพ์คำglobal
พันครั้งในอาชีพการงานของพวกเขา
นี้ยังเป็นวิธีที่ผมใช้สำหรับตัวอย่างข้างต้น แต่มันเป็นความชั่วร้าย
คำตอบนี้นานเกินไปแล้วที่จะให้ฉันอธิบายเพิ่มเติมว่าทำไม แต่การอ่านผลลัพธ์แรกใน SERP สำหรับ"ตัวแปรทั่วโลกที่ชั่วร้าย"เป็นจุดเริ่มต้นที่ดี
แต่เป็นไปได้อย่างไรที่จะหลีกเลี่ยงตัวแปรส่วนกลาง?
มีหลายวิธี
บางส่วนของคำตอบที่มีอายุมากกว่าที่นี่ใช้วิธีการเช่นแบบคงที่
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
มันง่ายและดี แต่มันบังคับให้ใช้รูปแบบสำหรับทุกคลาสที่เราต้องการเข้าถึง
ยิ่งไปกว่านั้นหลายครั้งที่วิธีนี้ทำให้เกิดปัญหาในคลาสพระเจ้าเพราะนักพัฒนาทำให้เข้าถึงคลาสหลักโดยใช้วิธีนี้แล้วใช้เพื่อเข้าถึงคลาสอื่นทั้งหมด
ฉันได้อธิบายแล้วว่าคลาสเทพนั้นแย่ขนาดไหนดังนั้นวิธีอินสแตนซ์แบบสแตติกจึงเป็นวิธีที่ดีที่จะไปเมื่อปลั๊กอินจำเป็นต้องเข้าถึงคลาสหนึ่งหรือสองคลาสเท่านั้น
นี่ไม่ได้หมายความว่ามันสามารถใช้สำหรับปลั๊กอินที่มีเพียงสองคลาสเท่านั้นในความเป็นจริงเมื่อใช้หลักการฉีดพึ่งพาอย่างถูกต้องมันเป็นไปได้ที่จะสร้างแอพพลิเคชั่นที่ซับซ้อนโดยไม่ต้องเข้าถึงทั่วโลกเป็นจำนวนมาก ของวัตถุ
อย่างไรก็ตามบางครั้งปลั๊กอินต้องทำให้บางคลาสสามารถเข้าถึงได้และในกรณีนั้นวิธีอินสแตนซ์แบบสแตติกจะมีจำนวนมาก
อีกวิธีที่เป็นไปได้คือการใช้รูปแบบการรีจิสทรี
นี่เป็นการใช้งานที่ง่ายมากของมัน:
namespace GM\WPSE\Example;
class Registry {
private $storage = array();
function add( $id, $class ) {
$this->storage[$id] = $class;
}
function get( $id ) {
return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
}
}
การใช้คลาสนี้เป็นไปได้ที่จะเก็บวัตถุในวัตถุรีจิสตรีด้วย id ดังนั้นการเข้าถึงรีจิสตรีจะเป็นไปได้ที่จะมีสิทธิ์เข้าถึงออบเจ็กต์ทั้งหมด แน่นอนเมื่อมีการสร้างวัตถุเป็นครั้งแรกจะต้องมีการเพิ่มลงในรีจิสทรี
ตัวอย่าง:
global $registry;
if ( is_null( $registry->get( 'tools' ) ) ) {
$tools = new GM\WPSE\Example\Tools;
$registry->add( 'tools', $tools );
}
if ( is_null( $registry->get( 'front' ) ) ) {
$front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );
$registry->add( 'front', front_stuff );
}
add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );
ตัวอย่างข้างต้นแสดงให้เห็นชัดเจนว่าเป็นประโยชน์ต่อสตรีที่ต้องสามารถเข้าถึงได้ทั่วโลก ตัวแปรโกลบอลสำหรับรีจิสตรีเพียงอย่างเดียวนั้นไม่เลวร้ายมากนักอย่างไรก็ตามสำหรับผู้ที่ไม่ได้เป็นโกลบอลก็สามารถใช้วิธีอินสแตนซ์แบบคงที่สำหรับรีจิสตรีหรืออาจเป็นฟังก์ชันที่มีตัวแปรแบบคงที่:
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
ในครั้งแรกที่มีการเรียกใช้ฟังก์ชั่นนี้จะเป็นการยกตัวอย่างรีจิสทรี
วิธีการเฉพาะ WordPress อีกวิธีในการทำให้คลาสที่สามารถเข้าถึงได้ทั่วโลกสามารถส่งคืนอินสแตนซ์ของวัตถุจากตัวกรองได้ บางสิ่งเช่นนี้
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
หลังจากนั้นทุกที่ต้องการรีจิสทรี:
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
รูปแบบที่สามารถนำมาใช้ก็คือรูปแบบการบริการระบุตำแหน่ง มันคล้ายกับรูปแบบรีจิสทรี แต่ตัวระบุตำแหน่งบริการจะถูกส่งผ่านไปยังคลาสต่างๆโดยใช้การฉีดพึ่งพา
ปัญหาหลักของรูปแบบนี้คือการซ่อนการพึ่งพาของคลาสทำให้รหัสยากต่อการบำรุงรักษาและอ่าน
ภาชนะ DI
ไม่ว่าจะใช้วิธีการใดในการเข้าถึงรีจิสตรีหรือตัวระบุตำแหน่งบริการทั่วโลกวัตถุต้องถูกจัดเก็บไว้ที่นั่นและก่อนที่จะถูกจัดเก็บวัตถุเหล่านั้นจะต้องได้รับการยกตัวอย่าง
ในแอพพลิเคชั่นที่ซับซ้อนที่มีคลาสค่อนข้างมากและส่วนใหญ่มีการพึ่งพาหลายคลาสอินสแตนซ์การสร้างอินสแตนซ์ต้องการรหัสจำนวนมากดังนั้นความเป็นไปได้ของข้อผิดพลาดที่เพิ่มขึ้น: รหัสที่ไม่มีอยู่จะไม่มีบั๊ก
ในปีที่ผ่านมามีห้องสมุด PHP บางส่วนที่ช่วยให้นักพัฒนา PHP สามารถอินสแตนซ์และจัดเก็บอินสแตนซ์ของวัตถุได้อย่างง่ายดายแก้ไขการพึ่งพาของพวกเขาโดยอัตโนมัติ
ไลบรารีนี้เป็นที่รู้จักกันในชื่อ Dependency Injection Containers เพราะพวกมันมีความสามารถในการสร้างอินสแตนซ์ของชั้นเรียนเพื่อแก้ไขการพึ่งพาและยังสามารถเก็บวัตถุและคืนพวกมันเมื่อจำเป็นซึ่งทำหน้าที่คล้ายกับวัตถุรีจิสตรี
โดยปกติเมื่อใช้คอนเทนเนอร์ DI นักพัฒนาจะต้องตั้งค่าการพึ่งพาสำหรับทุกคลาสของแอปพลิเคชันและจากนั้นในครั้งแรกที่จำเป็นต้องใช้คลาสในรหัสมันจะถูกสร้างอินสแตนซ์พร้อมการอ้างอิงที่เหมาะสมและอินสแตนซ์เดียวกัน .
บางตู้คอนเทนเนอร์ DI ยังมีความสามารถที่จะค้นพบการอ้างอิงโดยไม่ต้องกำหนดค่าโดยอัตโนมัติ แต่ใช้PHP สะท้อน
ภาชนะ DI บางที่รู้จักกันดีคือ:
และอื่น ๆ อีกมากมาย.
ฉันต้องการชี้ให้เห็นว่าสำหรับปลั๊กอินง่าย ๆ ที่เกี่ยวข้องกับคลาสและคลาสเพียงไม่กี่รายการที่มีจำนวนไม่มากอาจจะไม่คุ้มค่าที่จะใช้คอนเทนเนอร์ DI: วิธีอินสแตนซ์แบบคงที่หรือรีจิสตรีที่เข้าถึงได้ทั่วโลกเป็นโซลูชั่นที่ดี ประโยชน์ของ DI container นั้นชัดเจน
แน่นอนว่าแม้แต่คอนเทนเนอร์ DI ต้องสามารถเข้าถึงได้เพื่อใช้ในแอปพลิเคชันและเพื่อจุดประสงค์นั้นมันเป็นไปได้ที่จะใช้วิธีการใดวิธีการหนึ่งที่เห็นด้านบนตัวแปรโกลบอลตัวแปรอินสแตนซ์คงที่ตัวแปรกลับวัตถุผ่านตัวกรองเป็นต้น
นักแต่งเพลง
หากต้องการใช้ DI container มักจะหมายถึงการใช้รหัสบุคคลที่สาม ทุกวันนี้ใน PHP เมื่อเราจำเป็นต้องใช้ lib ภายนอก (ดังนั้นไม่เพียง แต่เก็บ DI แต่รหัสใด ๆที่ไม่ได้เป็นส่วนหนึ่งของแอปพลิเคชัน) เพียงแค่ดาวน์โหลดมันและวางไว้ในโฟลเดอร์แอปพลิเคชันของเรา แม้ว่าเราจะเป็นผู้เขียนโค้ดอีกชิ้น
decoupling รหัสโปรแกรมจากการพึ่งพาภายนอกเป็นสัญลักษณ์ขององค์กรที่ดีและความน่าเชื่อถือที่ดีขึ้นและดีกว่าที่มีสุขภาพจิตดีของรหัส
นักแต่งเพลงเป็นมาตรฐานจริงในชุมชน PHP ในการจัดการการพึ่งพา PHP ไกลออกไปเป็นกระแสหลักในชุมชน WP เช่นกันมันเป็นเครื่องมือที่นักพัฒนา PHP และ WordPress ทุกคนควรรู้อย่างน้อยถ้าไม่ได้ใช้
คำตอบนี้มีขนาดหนังสือแล้วเพื่อให้การอภิปรายเพิ่มเติมและการพูดคุยกับนักแต่งเพลงที่นี่อาจเป็นหัวข้อนอกมันถูกกล่าวถึงเพียงเพื่อความสมบูรณ์
สำหรับข้อมูลเพิ่มเติมโปรดเยี่ยมชมเว็บไซต์ของนักแต่งเพลงและก็ยังคุ้มค่าให้อ่านที่จะถึงนี้minisite curated โดย@Rarst
1 PSR เป็นกฎมาตรฐาน PHP ที่เผยแพร่โดยPHP Framework Interop Group
2นักแต่งเพลง (ห้องสมุดที่จะกล่าวถึงในคำตอบนี้) เหนือสิ่งอื่นใดนอกจากนี้ยังมีโปรแกรมอรรถประโยชน์โหลดอัตโนมัติ