呈上篇 “在WordPress中建立外掛的方法”
當外掛檔案被啟用並載入後,就可以在主檔案內開始撰寫PHP程式碼
WordPress並沒有規定要如何架構外掛程式碼(檔案架構、模組化等等),但有給出一個Code Style的規範,可參考 PHP Coding Standards, wordpress.org
可以直接就執行一些動作
<?php // register hook add_action('wp_head', 'octopuswp_foo_bar'); function octopuswp_foo_bar() { // do something in hooked function } // do something $order = wc_get_order(123); $order->update_meta_data('_foo', 'bar'); $order->save();
但,這樣的作法可能會有問題,以上列程式碼為例
- octopuswp_foo_bar 定義在全域下,可能會與其他第三方外掛的名稱衝突
- 用到了WooCommerce的wc_get_order,如果你的外掛在WooCommerce之前被載入,那這行就會出錯,因為找不到
wc_get_order
這個function,WordPress會依照資料夾名稱順序去載入外掛
考慮到WordPress的生態系,會用其他第三方外掛,都命名在全域下,會有名稱衝突的風險,因此最好的做法是OOP,將外掛包成一個類別
<?php // 判斷類別是否已經存在這個Class if(!class_exists('OctopusWP_Plugin')) { class OctopusWP_Plugin { public function __construct() { $this->register_hooks(); $order = wc_get_order(123); } /** * 註冊所有hooks */ private function register_hooks() { // hook後面給的都是一個"可被呼叫的"的函式名稱,因為foo_bar是這個類別的方法,所以必須要傳入這個類別的實體(instance),才可在外部被呼叫,且foo_bar必須為public add_action('wp_head', [$this, 'foo_bar']); } public function foo_bar() { // do something } } // 初始化 new OctopusWP_Plugin(); }
透過類別,來限制外掛的命名域,類別內方法重複並沒有關係,只要類別名稱不重複就好
呈上述問題2,這樣直接new出來的方式,例如上面程式碼的建構式內還是有用到了wc_get_order
,如果外掛在WooCommerce前被載入的話,還是會出錯,假如你的外掛是依賴在某個外掛下面,例如是一個WooCommerce用的金流外掛或是物流外掛,那最好是確保你的外掛載入在WooCommerce後,可以使用
<?php // 判斷類別是否已經存在這個Class if(!class_exists('OctopusWP_Plugin')) { class OctopusWP_Plugin { // 存放外掛類別的instance public static $instance; // 取得此類別的instance public static function get_instance() { // 判斷是否有初始化過 if(is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } public function __construct() { $this->register_hooks(); $order = wc_get_order(123); } /** * 註冊所有hooks */ private function register_hooks() { // hook後面給的都是一個"可被呼叫的"的函式名稱,因為foo_bar是這個類別的方法,所以必須要傳入這個類別的實體(instance),才可在外部被呼叫,且foo_bar必須為public add_action('wp_head', [$this, 'foo_bar']); } public function foo_bar() { // do something } } // 在特定的hook時間點再初始化外掛 // WooCommerce載入完之後會呼叫的action hook add_action('woocommerce_init', ['OctopusWP_Plugin', 'get_instance']); // 因為get_instance是static函式,所以只要給類別的名稱就行 // 所有外掛都被載入完之後會呼叫的action hook add_action('plugins_loaded', ['OctopusWP_Plugin', 'get_instance']); }
初始化的方法改成get_instance
的方式,可以直接定義在類別內並傳入action hook
初始化的時機點也不是在外掛檔案被載入時馬上初始化,而是再hook到某一個action,在action被呼叫時再初始化
當然,如果你的外掛沒有任何依賴任何其他外掛,或者下列情形,就是直接初始化就好
// 判斷類別是否已經存在這個Class if(!class_exists('OctopusWP_Plugin')) { class OctopusWP_Plugin { // 存放外掛類別的instance public static $instance; // 取得此類別的instance public static function get_instance() { // 判斷是否有初始化過 if(is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } public function __construct() { $this->register_hooks(); } /** * 註冊所有hooks */ private function register_hooks() { // hook後面給的都是一個"可被呼叫的"的函式名稱,因為foo_bar是這個類別的方法,所以必須要傳入這個類別的實體(instance),才可在外部被呼叫,且foo_bar必須為public add_action('wp_head', [$this, 'foo_bar']); } public function foo_bar() { // do something // 雖然這裡有用到wc_get_order,但是foo_bar方法是在wp_head被呼叫時才會呼叫的,而這時WooCommerce已經被載入完畢了,所以不會出錯 $order = wc_get_order(123); } } OctopusWP_Plugin::get_instance(); }
總之,這裡只要注意一下程式有載入順序,在外掛初始化時不能呼叫還沒被定義的方法或類別
但如果是在被hook的方法內,因在註冊hook時只會紀錄hook上去的方法資訊,在實際hook被呼叫時才會執行該方法