第 7 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 實施:管理 WordPress Hooks
已發表: 2022-02-04到目前為止,與插件 API 交互意味著在每個類的構造函數中調用add_action()
和add_filters()
。
到目前為止,這種方法已經足夠好了,因為它讓事情變得簡單,讓我們能夠專注於學習更多關於 WordPress 面向對象編程的知識。 然而,這並不理想。
如果一個對像在創建時註冊了它的所有鉤子,那麼單元測試之類的事情就會變得棘手。
注意:單元測試應該單獨測試每個“單元”。 即使您現在不編寫單元測試,如果您決定編寫測試,編寫可測試的代碼也會為您節省大量的重構時間。
掛鉤經理
讓我們更進一步,引入一個新類來管理我們的鉤子,我們稱之為Hooks_Manager
。 這個類將負責註冊我們所有的鉤子。 因此,我們將使用register()
方法創建一個新類。
class Hooks_Manager { /** * Register the hooks of the given object. * * @param object $object */ public function register( $object ) { // Register the hooks the specified object needs } }
對於每個需要註冊鉤子來實現的類,我們都需要一個接口。
interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); }
您可以將接口視為合約,其中實現該接口的類是“契約綁定”以實現該接口中定義的所有方法。
例如,掛鉤到login_head
操作的Login_Error
類必須實現Hooks
接口的get_actions()
方法。
class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } }
Hooks_Manager
的register()
方法接受一個對象,調用它的get_actions()
方法並註冊它的所有動作。
public function register( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } }
讓我們在接口中添加一個get_filters()
方法,這樣我們就可以註冊動作和過濾器。
interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); /** * Return the filters to register. * * @return array */ public function get_filters(); }
回到我們的Login_Error
類,我們需要實現這個新的get_filters()
方法。
class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }
我們將Hooks_Manager
的register()
方法重命名為register_actions()
。 我們還將添加一個register_filters()
方法。 這兩個方法將分別負責註冊動作和過濾器。
class Hooks_Manager { /** * Register the actions of the given object. * * @param object $object */ private function register_actions( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } } /** * Register the filters of the given object. * * @param object $object */ private function register_filters( $object ) { $filters = $object->get_filters(); foreach ( $filters as $filter_name => $filter_details ) { $method = $filter_details[0]; $priority = $filter_details[1]; $accepted_args = $filter_details[2]; add_filter( $filter_name, array( $object, $method ), $priority, $accepted_args ); } } }
現在我們可以再次添加一個register()
方法,它只是調用register_actions()
和register_filters()
。
class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { $this->register_actions( $object ); $this->register_filters( $object ); } // ...
如果一個類不需要同時註冊動作和過濾器怎麼辦? Hooks
接口包含兩個方法: get_actions()
和get_filters()
。 所有實現該接口的類都將被迫實現這兩種方法。
class Cookie_Login implements Hooks { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } public function get_filters() { return array(); } }
例如, Cookie_Login
類必須只註冊操作,但現在它被迫實現get_filters()
方法只是為了返回一個空數組。

接口隔離原則 (ISP) ,即 SOLID 中的“I”,規定:
“任何客戶都不應該被迫依賴它不使用的方法。”
這意味著我們現在正在做的正是我們不應該做的。
接口隔離
我們可以通過將接口拆分成更小、更具體的接口來解決這個問題,這樣我們的類只需要知道它們感興趣的方法。
interface Actions { /** * Return the actions to register. * * @return array */ public function get_actions(); }
interface Filters { /** * Return the filters to register. * * @return array */ public function get_filters(); }
我們不再需要get_actions()
和get_filters()
了,我們可以只實現Actions
接口並擺脫get_filters()
class Cookie_Login implements Actions { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } }
另一方面,需要動作和過濾器的Login_Error
只需要實現這兩個接口。 類可以通過用逗號分隔多個接口來實現多個接口。
class Login_Error implements Actions, Filters { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }
現在我們已經分離了我們的接口,我們只需要更新Hooks_Manager
的register()
方法來反映我們的變化。
class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { if ( $object instanceof Actions ) { $this->register_actions( $object ); } if ( $object instanceof Filters ) { $this->register_filters( $object ); } } // ...
這樣,我們根據指定對象實現的接口有條件地僅調用register_actions()
、僅register_filters()
或兩者。
要實際使用鉤子管理器:
$hooks_manager = new Hooks_Manager(); $hooks_manager->register( $login_error ); $hooks_manager->register( $cookie_login );
而已! 我們現在可以使用該對象來管理整個代碼庫的鉤子。
結論
當然,有幾種方法可以以面向對象的方式管理您的鉤子,我們只是向您展示了其中一種。 您應該嘗試並找到適合您需求的產品。
在本系列的最後一部分,我們將看到如何以面向對象的方式處理選項,討論封裝、抽像以及如何解耦類以創建易於擴展的靈活插件!
單擊此處閱讀面向對象編程系列的第 8 部分
也可以看看
- WordPress 和麵向對象的編程——概述
- 第 2 部分 – WordPress 和麵向對象編程:一個真實世界的示例
- 第 3 部分 – WordPress 和麵向對象編程:A WordPress 示例 – 定義範圍
- 第 4 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 設計
- 第 5 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 實現:管理菜單
- 第 6 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 實現:註冊部分