第 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_Managerregister()方法接受一個對象,調用它的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_Managerregister()方法重命名為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_Managerregister()方法來反映我們的變化。

 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 示例 – 實現:註冊部分