在本系列中,我們專注於建立可維護的 WordPress 元框。我的意思是,我們一直在努力創建一個組織良好、遵循 WordPress 編碼標準的 WordPress 插件,並且隨著專案的不斷進展,可以輕鬆調整和維護。
儘管我們已經實施了一些好的實踐,但仍然有重構的空間。對於這個系列,這是設計使然的。每當您為客戶或大公司開發專案時,您必須維護現有程式碼庫的可能性相當高。因此,我希望我們能夠返回到我們的程式碼庫,以便改進我們編寫的一些程式碼。
請注意,本文不會按照其他文章的格式編寫 - 也就是說,不會採用「首先我們這樣做,然後我們這樣做」的開發方法。相反,我們將突出顯示需要重構的幾個領域,然後獨立於我們正在進行的其他更改來處理它們。
需要明確的是,重構行為(依照維基百科的定義)是:#
##重構改進了軟體的非功能屬性。優點包括提高程式碼可讀性和降低複雜性以提高原始程式碼可維護性,並創建更具表現力的內部架構或物件模型以提高可擴展性。簡而言之,它使程式碼更具可讀性、更簡單、更易於遵循,並且從最終用戶的角度來看,這一切都無需改變程式碼的行為。
這可以透過多種不同的方式來實現,每種方式對於給定的項目都是獨特的。在我們的例子中,我們將考慮重構我們的建構子、一些保存方法、一些輔助方法等等。
最終,我們的目標是展示一些可以在您未來的 WordPress 工作中使用的策略。我的目標是在本文中盡可能詳細地介紹;但是,請注意,可能存在未涵蓋的其他重構機會。
如果是這樣的話,那就太好了!請隨意在您自己的程式碼庫實例上製作它們。話雖如此,讓我們開始吧。
建構子
<?php public function __construct( $name, $version ) { $this->name = $name; $this->version = $version; $this->meta_box = new Authors_Commentary_Meta_Box(); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); }請注意,它目前正在執行兩件事:
應該使用建構函式來初始化與給定類別相關的所有屬性,以便當使用者實例化一個類別時,他/她擁有使用該類別所需的一切。
由於他們可能不想在初始化類別時註冊鉤子,因此我們需要將其抽象化為自己的
initialize_hooks 方法。我們的程式碼現在應該如下所示:
<?php public function __construct( $name, $version ) { $this->name = $name; $this->version = $version; $this->meta_box = new Authors_Commentary_Meta_Box(); } public function initialize_hooks() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); }之後,我們需要確保更新authors-commentary.php的核心程式碼,以便它正確實例化並註冊鉤子。
<?php function run_author_commentary() { $author_commentary = new Author_Commentary_Admin( 'author-commentary', '1.0.0' ); $author_commentary->initialize_hooks(); } run_author_commentary();這裡,主要差異在於我們更新了傳遞給主類別的版本號,而且我們還在
initialize_hooks<code class="的上下文中显式调用"> 函數「內聯」>run_author_commentary
。
如果您現在執行程式碼,一切都應該與重構之前完全一樣。 我還想補充一點,您可以有一個單獨的類別負責協調掛鉤和回調,以便該責任位於單獨的類別中。儘管我很喜歡這種方法,但這超出了本文的範圍。
接下來,讓我們對
class-authors-commentary-meta-box.php 執行相同的操作。我們可以簡單地重新命名構造函數,而不是創建一個新函數,因為構造函數實際上並沒有執行任何操作。這意味著我們的程式碼應該如下所示:
<?php public function __construct() { add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); add_action( 'save_post', array( $this, 'save_post' ) ); }對此:
<?php public function initialize_hooks() { add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); add_action( 'save_post', array( $this, 'save_post' ) ); }我們需要做的最後一個更改是更新主類別中的建構函數,以便它現在讀取我們在主插件類別中創建的
initialize_hooks 函數的內部。
<?php public function initialize_hooks() { $this->meta_box->initialize_hooks(); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); }再次刷新頁面,您的外掛程式應該仍然可以像重構之前一樣正常運行。 #輔助方法
Authors_Commentary_Meta_Box 類別中,我們在
save_post 函數中有許多非常多餘的條件。發生這種情況時,通常意味著大部分功能可以抽象化為輔助函數,然後從最初放置它們的函數內部呼叫。
<?php public function save_post( $post_id ) { /* If we're not working with a 'post' post type or the user doesn't have permission to save, * then we exit the function. */ if ( ! $this->is_valid_post_type() || ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) { return; } // If the 'Drafts' textarea has been populated, then we sanitize the information. if ( ! empty( $_POST['authors-commentary-drafts'] ) ) { // We'll remove all white space, HTML tags, and encode the information to be saved $drafts = trim( $_POST['authors-commentary-drafts'] ); $drafts = esc_textarea( strip_tags( $drafts ) ); update_post_meta( $post_id, 'authors-commentary-drafts', $drafts ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-drafts', true ) ) { delete_post_meta( $post_id, 'authors-commentary-drafts' ); } } // If the 'Resources' inputs exist, iterate through them and sanitize them if ( ! empty( $_POST['authors-commentary-resources'] ) ) { $resources = $_POST['authors-commentary-resources']; $sanitized_resources = array(); foreach ( $resources as $resource ) { $resource = esc_url( strip_tags( $resource ) ); if ( ! empty( $resource ) ) { $sanitized_resources[] = $resource; } } update_post_meta( $post_id, 'authors-commentary-resources', $sanitized_resources ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-resources', true ) ) { delete_post_meta( $post_id, 'authors-commentary-resources' ); } } // If there are any values saved in the 'Published' input, save them if ( ! empty( $_POST['authors-commentary-comments'] ) ) { update_post_meta( $post_id, 'authors-commentary-comments', $_POST['authors-commentary-comments'] ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-comments', true ) ) { delete_post_meta( $post_id, 'authors-commentary-comments' ); } } }除了開始的方法太長之外,我們還可以清理一些東西:
和邏輯
OR 運算子的初始條件
陣列中是否存在資訊的條件
第一个条件检查的目的是确保当前用户能够将数据保存到给定的帖子。现在,我们实际上是在检查当前帖子类型是否是有效的帖子类型,以及用户是否有权保存给定 WordPress 传递的当前随机数值。
现在,代码如下:
如果这不是有效的帖子类型或用户没有保存权限,则退出此功能。
这并不是很糟糕,但绝对可以改进。让我们将其合并到单个评估中,而不是使用 OR
,使其显示为:
如果用户没有保存权限,则退出此功能。
幸运的是,这是一个相对容易的修复。由于保存的帖子类型有助于确定用户是否有权保存帖子,因此我们可以将该逻辑移至 user_can_save
函数中。
因此,让我们将 is_valid_post_type
函数移至 user_can_save
函数中:
<?php private function user_can_save( $post_id, $nonce_action, $nonce_id ) { $is_autosave = wp_is_post_autosave( $post_id ); $is_revision = wp_is_post_revision( $post_id ); $is_valid_nonce = ( isset( $_POST[ $nonce_action ] ) && wp_verify_nonce( $_POST[ $nonce_action ], $nonce_id ) ); // Return true if the user is able to save; otherwise, false. return ! ( $is_autosave || $is_revision ) && $this->is_valid_post_type() && $is_valid_nonce; }
现在,负责确定用户是否可以保存帖子元数据的所有逻辑都封装在专门设计用于精确评估的函数中。
我们从这个开始:
<?php if ( ! $this->is_valid_post_type() || ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) { return; }
现在我们有这个:
<?php if ( ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) { return; }
阅读起来容易多了,不是吗?
接下来,在开始清理、验证和保存(或删除)元数据之前,我们将检查 $_POST
集合以确保数据确实存在。
我们可以编写一个小的辅助函数来为我们处理这个评估。虽然我们本质上是编写了一些代码,使我们的评估更加冗长,但与我们直接保留它们相比,条件语句读起来会更清晰一些。
首先,引入以下函数(注意它接受一个参数):
<?php /** * Determines whether or not a value exists in the $_POST collection * identified by the specified key. * * @since 1.0.0 * * @param string $key The key of the value in the $_POST collection. * @return bool True if the value exists; otherwise, false. */ private function value_exists( $key ) { return ! empty( $_POST[ $key ] ); }
接下来,重构最初调用 的所有调用!空( $_POST[ ... ] )
以便他们利用此功能。
例如,函数调用应如下所示:
if ( $this->value_exists( 'authors-commentary-comments' ) ) { // ... } else { // ... }
请注意,在该函数中放置的整个条件中,如果值不存在,则每次删除帖子元数据的评估看起来都完全相同。
例如,我们每次都会看到这样的东西:
<?php if ( '' !== get_post_meta( $post_id, 'authors-commentary-comments', true ) ) { delete_post_meta( $post_id, 'authors-commentary-comments' ); }
这显然是重构代码的机会。因此,让我们创建一个名为 delete_post_meta
的新函数,并让它封装所有这些信息:
<?php /** * Deletes the specified meta data associated with the specified post ID * based on the incoming key. * * @since 1.0.0 * @access private * @param int $post_id The ID of the post containing the meta data * @param string $meta_key The ID of the meta data value */ private function delete_post_meta( $post_id, $meta_key ) { if ( '' !== get_post_meta( $post_id, $meta_key, true ) ) { delete_post_meta( $post_id, '$meta_key' ); } }
现在我们可以返回并替换所有 else 条件评估以调用此单个函数,使其读取如下内容:
<?php // If the 'Drafts' textarea has been populated, then we sanitize the information. if ( $this->value_exists( 'authirs-commentary-drafts' ) ) { // We'll remove all white space, HTML tags, and encode the information to be saved $drafts = trim( $_POST['authors-commentary-drafts'] ); $drafts = esc_textarea( strip_tags( $drafts ) ); update_post_meta( $post_id, 'authors-commentary-drafts', $drafts ); } else { $this->delete_post_meta( $post_id, 'authors-commentary-drafts' ); }
此时,我们实际上只有这部分代码的另一个方面需要重构。
现在,保存帖子元数据的方式是通过评估 $_POST
集合中数据是否存在的过程来完成的,并根据信息类型对其进行清理,然后将其保存到帖子元数据中。
理想情况下,我们希望在自己的函数中清理数据,并将帖子元数据保存在自己的函数中。因此,我们需要引入新的功能。
首先,让我们进行消毒工作。因为我们正在处理 textareas
和数组,所以我们需要通过几种方法来处理清理调用。由于我们要么使用数组,要么不使用数组,所以我们可以创建一个函数,该函数接受一个可选参数,表示我们是否正在使用数组。
如果我们不使用数组,那么我们会将传入的数据视为文本;否则,我们会将其视为数组:
<?php /** * Sanitizes the data in the $_POST collection identified by the specified key * based on whether or not the data is text or is an array. * * @since 1.0.0 * @access private * @param string $key The key used to retrieve the data from the $_POST collection. * @param bool $is_array Optional. True if the incoming data is an array. * @return array|string The sanitized data. */ private function sanitize_data( $key, $is_array = false ) { $sanitized_data = null; if ( $is_array ) { $resources = $_POST[ $key ]; $sanitized_data = array(); foreach ( $resources as $resource ) { $resource = esc_url( strip_tags( $resource ) ); if ( ! empty( $resource ) ) { $sanitized_data[] = $resource; } } } else { $sanitized_data = ''; $sanitized_data = trim( $_POST[ $key ] ); $sanitized_data = esc_textarea( strip_tags( $sanitized_data ) ); } return $sanitized_data; }
接下来,我们可以更新清理调用以使用此方法。但在此之前,我们还需要编写一个小助手,负责使用经过净化的输入更新帖子元数据:
<?php private function update_post_meta( $post_id, $meta_key, $meta_value ) { if ( is_array( $_POST[ $meta_key ] ) ) { $meta_value = array_filter( $_POST[ $meta_key ] ); } update_post_meta( $post_id, $meta_key, $meta_value ); }
现在我们可以更新之前在函数中使用的所有条件,如下所示:
<?php public function save_post( $post_id ) { if ( ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) { return; } if ( $this->value_exists( 'authors-commentary-drafts' ) ) { $this->update_post_meta( $post_id, 'authors-commentary-drafts', $this->sanitize_data( 'authors-commentary-drafts' ) ); } else { $this->delete_post_meta( $post_id, 'authors-commentary-drafts' ); } if ( $this->value_exists( 'authors-commentary-resources' ) ) { $this->update_post_meta( $post_id, 'authors-commentary-resources', $this->sanitize_data( 'authors-commentary-resources', true ) ); } else { $this->delete_post_meta( $post_id, 'authors-commentary-resources' ); } if ( $this->value_exists( 'authors-commentary-comments' ) ) { $this->update_post_meta( $post_id, 'authors-commentary-comments', $_POST['authors-commentary-comments'] ); } else { $this->delete_post_meta( $post_id, 'authors-commentary-comments' ); } }
请注意,我们实际上可以进一步重构这个特定的部分,这样就没有那么多的条件,但是考虑到文章的长度、时间的长度,并且还尝试引入一些其他策略,这将是留作练习,在您自己的时间完成。
到目前为止,我们已经完成了我们的插件。我们编写了一个插件,引入了一个元框,为撰写博客文章的作者提供选项。
此外,我们还采用了 WordPress 编码标准、一些强大的文件组织策略,并创建了许多辅助方法和抽象,这将帮助我们在未来的开发中维护这个特定的插件。
由于突出显示每一个重构机会并不容易,因此可能还需要进行其他更改。在您自己的时间里,请随意尝试自己实现其中一些。
总的来说,我希望您喜欢本系列并从中学到很多东西,并且我希望它能帮助您在未来基于 WordPress 的项目中编写更好、更易于维护的代码。
以上是重構:增強 WordPress 元框以實現長期維護的詳細內容。更多資訊請關注PHP中文網其他相關文章!