วิธีที่ดีที่สุดในการเริ่มเรียนในปลั๊กอิน WP?


89

ฉันได้สร้างปลั๊กอินและแน่นอนว่าเป็นฉันฉันต้องการไปด้วยวิธีการที่ดี OO ตอนนี้สิ่งที่ฉันได้ทำคือการสร้างชั้นนี้แล้วด้านล่างสร้างตัวอย่างของชั้นนี้:

class ClassName {

    public function __construct(){

    }
}

$class_instance = new ClassName();  

ฉันสมมติว่ามีวิธี WP มากกว่าที่จะเริ่มชั้นนี้แล้วฉันเจอคนที่บอกว่าพวกเขาชอบที่จะมีinit()ฟังก์ชั่นมากกว่า__construct()หนึ่ง และฉันก็พบว่ามีคนเพียงไม่กี่คนที่ใช้ตะขอต่อไปนี้:

class ClassName {

    public function init(){

    }
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );

อะไรคือวิธีที่ดีที่สุดในการสร้างอินสแตนซ์คลาส WP บนโหลดและมีสิ่งนี้เป็นตัวแปรที่เข้าถึงได้ทั่วโลก?

หมายเหตุ:ในฐานะที่เป็นจุดที่น่าสนใจฉันได้สังเกตเห็นว่าในขณะที่register_activation_hook()สามารถถูกเรียกจากภายใน__constructมันไม่สามารถเรียกได้จากภายในinit()โดยใช้ตัวอย่างที่สอง บางทีใครบางคนสามารถสอนฉันในจุดนี้

แก้ไข:ขอบคุณสำหรับคำตอบทั้งหมดเห็นได้ชัดว่ามีการถกเถียงกันพอสมควรว่าจะจัดการกับการเริ่มต้นในชั้นเรียนadd_action( 'plugins_loaded', ...);ได้อย่างไร

แก้ไข:เพื่อสร้างความสับสนให้กับเรื่องต่าง ๆ ฉันได้เห็นสิ่งนี้ใช้แล้ว (แม้ว่าฉันจะไม่ใช้วิธีนี้ด้วยตัวเองเพราะการเปลี่ยนคลาส OO ให้เป็นฟังก์ชั่นดูเหมือนจะเอาชนะจุดนั้นได้):

// Start up this plugin
add_action( 'init', 'ClassName' );
function ClassName() {
    global $class_name;
    $class_name = new ClassName();
}

1
เกี่ยวกับการแก้ไขครั้งล่าสุดถ้ามันมีอยู่ในไฟล์ปลั๊กอินเดียวกันกับคลาสมันจะไร้ประโยชน์ คุณอาจยกตัวอย่างคลาสได้ตามวิธีที่ฉันอธิบาย แม้ว่ามันจะอยู่ในไฟล์ที่แยกจากกันมันก็ยังค่อนข้างไร้สาระ ใช้กรณีที่ฉันเห็นเท่านั้นคือถ้าคุณต้องการสร้างฟังก์ชั่น wrapper ที่ช่วยให้คุณสามารถยกตัวอย่างชั้นเรียนนอกไฟล์ปลั๊กอินของคุณภายในธีมและอื่น ๆ ถึงกระนั้นฉันก็ต้องถามว่าตรรกะอะไรที่วางอยู่ข้างหลังเพราะการใช้เงื่อนไขและตะขอที่เหมาะสมควรอนุญาตให้ควบคุมเกรนละเอียดในการสร้างอินสแตนซ์ช่วยให้คุณสามารถเน้นการใช้ปลั๊กอินแทน
อดัม

ฉันเห็นด้วยกับสิ่งนี้ แต่ฉันคิดว่ามันคุ้มค่าที่จะใส่เพราะฉันพบมันในปลั๊กอิน WP สองตัว
kalpaitch

คำตอบ:


60

คำถามที่ดีมีหลายวิธีและขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ

ฉันมักจะทำ

add_action( 'plugins_loaded', array( 'someClassy', 'init' ));

class someClassy {

    public static function init() {
        $class = __CLASS__;
        new $class;
    }

    public function __construct() {
           //construct what you see fit here...
    }

    //etc...
}

อย่างละเอียดมากขึ้นตัวอย่างแบบเจาะลึกเกี่ยวกับที่มาเป็นผลของการอภิปรายที่ผ่านมาบางส่วนในหัวข้อนี้มากภายในห้องแชทที่สามารถมองเห็นในกระทู้นี้โดยสมาชิก WPSE toscho

วิธีการสร้างที่ว่างเปล่า

นี่คือข้อความที่ตัดตอนมาจากข้อดี / ข้อเสียที่นำมาจากส่วนสำคัญข้างต้นซึ่งเป็นตัวอย่างของวิธีการสร้างที่ว่างเปล่าในเต็ม

  • ข้อดี:

    • การทดสอบหน่วยสามารถสร้างอินสแตนซ์ใหม่โดยไม่ต้องเปิดใช้งาน hooks โดยอัตโนมัติ ไม่มีซิงเกิล

    • ไม่ต้องใช้ตัวแปรทั่วโลก

    • ใครก็ตามที่ต้องการทำงานกับอินสแตนซ์ปลั๊กอินสามารถโทร T5_Plugin_Class_Demo :: get_instance () ได้

    • ปิดการใช้งานง่าย

    • ยังคงเป็นจริง OOP: ไม่มีวิธีการทำงานแบบคงที่

  • ข้อด้อย:

    • อาจจะอ่านยากขึ้น?

ข้อเสียในความเห็นของฉันคือคนที่อ่อนแอซึ่งเป็นเหตุผลว่าทำไมมันต้องเป็นแนวทางที่ฉันชอบ แต่ไม่ใช่คนเดียวที่ฉันใช้ ในความเป็นจริงแล้วน้ำหนักหนักอื่น ๆ อีกมากมายจะไม่มีข้อกังขาในหัวข้อนี้เมื่อพวกเขาเข้ามามีส่วนร่วมในไม่ช้าเพราะมีความคิดเห็นที่ดีรอบ ๆ หัวข้อที่ควรเปล่ง


หมายเหตุ: ฉันต้องการค้นหาตัวอย่างส่วนสำคัญจากtoschoที่วิ่งผ่านการเปรียบเทียบ 3 หรือ 4 ของวิธีสร้างอินสแตนซ์ของคลาสภายในปลั๊กอินที่ดูข้อดีข้อเสียของแต่ละอันซึ่งลิงค์ด้านบนเป็นวิธีที่ได้รับความนิยม แต่ ตัวอย่างอื่นให้ความแตกต่างที่ดีกับหัวข้อนี้ หวังว่าtoschoยังคงมีอยู่ในไฟล์

หมายเหตุ: WPSE ตอบกระทู้นี้ด้วยตัวอย่างที่เกี่ยวข้องและการเปรียบเทียบ ยังเป็นทางออกที่ดีที่สุดเช่นคลาสใน WordPress

add_shortcode( 'baztag', array( My_Plugin::get_instance(), 'foo' ) );
class My_Plugin {

    private $var = 'foo';

    protected static $instance = NULL;

    public static function get_instance() {

        // create an object
        NULL === self::$instance and self::$instance = new self;

        return self::$instance; // return the object
    }

    public function foo() {

        return $this->var; // never echo or print in a shortcode!
    }
}

สิ่งที่จะเป็นความแตกต่างระหว่าง add_action ('plugins_loaded', ... ); และ add_action ('load-plugins.php', ... ); ตัวอย่างที่ฉันเอามาใช้หลัง
kalpaitch

1
จากสิ่งที่ฉันเข้าใจ load-plugins.php ถึงแม้ว่ามันจะทำงานเกี่ยวข้องกับupdate.phpไฟล์core และไม่ได้เป็นส่วนหนึ่งของการกระทำเริ่มต้นตามปกติที่ควรเชื่อถือเมื่อเกี่ยวข้องกับลำดับเหตุการณ์ที่เกิดขึ้นในระหว่างการเริ่มต้นและเหตุผลที่ฉันต้องการ เพื่อใช้ hooks เหล่านั้นที่ใช้ในกรณีplugins_loadedนี้ นี่คือสิ่งที่ผมมักจะพูดถึงภาพรวมอย่างรวดเร็วของสิ่งที่เกิดขึ้นเมื่ออ้างอิงการดำเนินการ คำอธิบายของฉันไม่สมบูรณ์ทั้งหมด
อดัม

4
ฉันชอบแนวทางแบบซิงเกิลนี้ อย่างไรก็ตามฉันถามใช้ plugins_loaded เป็นตะขอการเริ่มต้นของคุณ เบ็ดนี้มีขึ้นเพื่อให้ทำงานหลังจากที่โหลดปลั๊กอินทั้งหมดแล้ว ด้วยการเชื่อมโยงเข้าหลังจากนั้นคุณก็จะทำการไฮแจ็กและอาจสนุกไปกับความขัดแย้งหรือปัญหาลำดับการเริ่มต้นกับปลั๊กอินหรือธีมอื่น ๆ ที่เชื่อมโยงเข้ากับ plugins_loaded ฉันจะไม่ขอดำเนินการใด ๆ เพื่อเรียกใช้วิธีการเริ่มต้นของคุณ สถาปัตยกรรมปลั๊กอินได้รับการออกแบบเพื่อให้ทำงานแบบอินไลน์ไม่ใช่ในการดำเนินการ
Tom Auger

2
โปรดทราบว่าหากคุณใช้register_activation_hook()คุณจำเป็นต้องเรียกใช้ฟังก์ชันนั้นก่อนที่plugins_loadedการดำเนินการจะเริ่มขึ้น
Geert

1
เป็นข้อมูลเพิ่มเติมดูโพสต์นี้จาก @mikeschinkel และ dicuss ในความคิดเห็น hardcorewp.com/2012/…
bueltge

78

มาถึงที่นี่ 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นักแต่งเพลง (ห้องสมุดที่จะกล่าวถึงในคำตอบนี้) เหนือสิ่งอื่นใดนอกจากนี้ยังมีโปรแกรมอรรถประโยชน์โหลดอัตโนมัติ


1
หมายเหตุเพิ่มเติมว่า PHP 5.3 ก็เป็นจุดสิ้นสุดของชีวิตเช่นกัน โฮสต์ที่รับผิดชอบจะเสนออย่างน้อย 5.4 เป็นตัวเลือกหากไม่ใช่ค่าเริ่มต้น
Tom J Nowell

2
"ตั้งแต่วันที่ 14 สิงหาคม 2557 PHP 5.3 ถึงจุดสิ้นสุดของชีวิตแล้วมันก็ตายไปแล้วอย่างแน่นอน" เป็นบรรทัดแรกภายใต้"PHP 5.2 สำหรับซอมบี้" @TomJNowell
gmazzap

3
แค่สงสัยว่าคุณจะชี้ให้เห็น"สิ่งต่างๆมากมาย" เป็นอย่างไร? ;-)
MikeSchinkel

ในความพยายามที่จะหลีกเลี่ยงตัวแปรทั่วโลกฉันชอบแนวคิดของรูปแบบการลงทะเบียนด้วยฟังก์ชั่นและตัวแปรแบบคงที่ ครั้งแรกที่มีการเรียกใช้ฟังก์ชั่นนี้จะเป็นการยกตัวอย่างรีจิสทรี
Michael Ecklund

10

ฉันใช้โครงสร้างต่อไปนี้:

Prefix_Example_Plugin::on_load();

/**
 * Example of initial class-based plugin load.
 */
class Prefix_Example_Plugin {

    /**
     * Hooks init (nothing else) and calls things that need to run right away.
     */
    static function on_load() {

        // if needed kill switch goes here (if disable constant defined then return)

        add_action( 'init', array( __CLASS__, 'init' ) );
    }

    /**
     * Further hooks setup, loading files, etc.
     *
     * Note that for hooked methods name equals hook (when possible).
     */
    static function init(  ) {


    }
}

หมายเหตุ:

  • ได้กำหนดสถานที่สำหรับสิ่งที่ต้องวิ่งหนีทันที
  • ปิดการใช้งาน / แทนที่สำหรับปรับแต่งเป็นเรื่องง่าย (ปลดตะขอหนึ่งinitวิธี)
  • ฉันไม่คิดว่าฉันเคยใช้ / ต้องการวัตถุของคลาสปลั๊กอิน - ต้องติดตามมัน ฯลฯ ; นี่เป็นการกำหนดชื่อปลอมโดยเจตนาไม่ใช่ OOP (ส่วนใหญ่แล้ว)

ข้อจำกัดความรับผิดชอบฉันยังไม่ได้ใช้การทดสอบหน่วย ( หลายสิ่งหลายอย่างบนแผ่นโลหะ ) และฉันได้ยินว่าสแตติกสามารถเป็นที่นิยมน้อยกว่าสำหรับพวกเขา ทำวิจัยของคุณเกี่ยวกับเรื่องนี้ถ้าคุณต้องการทดสอบหน่วย


3
ฉันรู้ว่าผู้คนจำนวนมากในการทดสอบหน่วยไม่ชอบโซลูชั่นแบบคงที่ / ซิงเกิล ฉันคิดว่าหากคุณเข้าใจอย่างถ่องแท้ว่าคุณกำลังพยายามทำอะไรให้สำเร็จด้วยการใช้สแตติกและอย่างน้อยก็ตระหนักถึงการแตกหักของการทำเช่นนั้น หัวข้อที่ดีรอบนี้ที่Stack Overflow
อดัม

นี่ทำให้ฉันคิดจริงๆ เหตุใดจึงต้องใช้คลาสแล้วไม่ใช่แค่กลับไปใช้ฟังก์ชั่นนำหน้าอย่างง่าย เราทำเช่นนี้เพื่อให้ชื่อฟังก์ชัน / เมธอดสะอาดขึ้นหรือไม่? ฉันหมายถึงการให้พวกมันซ้อนกันกับ "คงที่" b4 พวกเขามันเป็นสิ่งที่อ่านได้มากขึ้น? โอกาสที่จะมีความขัดแย้งของชื่อนั้นเกี่ยวกับชื่อชั้นเดียวถ้าคุณใช้คำนำหน้า propper หรือว่าฉันทำอะไรหายไป?
James Mitch

1
@JamesMitch ใช่วิธีการแบบคงที่ทั้งหมดส่วนใหญ่เป็นเพียงฟังก์ชั่นที่มี namespace ปลอมที่ใช้ใน WP อย่างไรก็ตามคลาสมีข้อได้เปรียบเหนือฟังก์ชั่นที่บริสุทธิ์แม้ในกรณีนี้เช่น autoload และการสืบทอด เมื่อเร็ว ๆ นี้ฉันได้ย้ายจากวิธีการคงที่และต่อวัตถุอินสแตนซ์จริงที่จัดโดยภาชนะฉีดพึ่งพา
Rarst

3

ทุกอย่างขึ้นอยู่กับการใช้งาน

ฉันเคยทำปลั๊กอินที่สคริปต์ที่ลงทะเบียนเมื่อตัวสร้างถูกเรียกดังนั้นฉันต้องขอมันที่wp_enqueue_scriptsตะขอ

หากคุณต้องการเรียกมันเมื่อfunctions.phpไฟล์ของคุณถูกโหลดคุณอาจสร้างอินสแตนซ์ของตัวเอง$class_instance = new ClassName();ตามที่คุณพูดถึง

คุณอาจต้องการพิจารณาความเร็วและการใช้หน่วยความจำ ฉันไม่รู้อะไรเลย แต่ฉันสามารถจินตนาการได้ว่ามีตะขอที่ไม่ได้ทำในบางกรณี โดยการสร้างอินสแตนซ์ของคุณที่ hook นั้นคุณอาจบันทึกทรัพยากรของเซิร์ฟเวอร์


ขอบคุณมากสำหรับสิ่งนี้ฉันคิดว่ามีสองประเด็นสำหรับคำถามข้างต้นเช่นกัน อีกสิ่งหนึ่งว่า __ โครงสร้างเหมาะสมหรือไม่ init () เป็นวิธีที่ดีกว่าในการเริ่มต้นชั้นเรียน
kalpaitch

1
ฉันจะใช้init()วิธีสแตติกดังนั้นคลาสอินสแตนซ์จะถูกเรียกในขอบเขตของคลาสแทนขอบเขตอื่นที่คุณสามารถเขียนทับตัวแปรที่มีอยู่ได้
ทิมเอส

0

ฉันรู้ว่านี่เป็นสองสามปีที่ผ่านมา แต่ในขณะที่php 5.3 สนับสนุนวิธีการที่ไม่ระบุชื่อดังนั้นฉันจึงมากับสิ่งนี้:

add_action( 'plugins_loaded', function() { new My_Plugin(); } );

และอย่างใดฉันชอบมันมากที่สุด ฉันสามารถใช้ตัวสร้างแบบปกติและไม่จำเป็นต้องกำหนดวิธี "init" หรือ "on_load" ใด ๆ ที่ทำให้โครงสร้าง OOP ของฉันยุ่งเหยิง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.