探究 WordPress `wp_delete_post()` 函数的源码:如何删除文章,并同时删除相关的元数据、评论和附件。

各位观众老爷,晚上好!我是你们的老朋友,人称“代码界的段子手”——阿码。今天咱要扒一扒 WordPress 里面一个“杀人不眨眼”的函数:wp_delete_post()。别害怕,我说的“杀人”是杀帖子,而且是“杀”得干干净净的那种!

引子:故事从这里开始

话说江湖上流传着这么一句话:“WordPress 虽好,删帖要谨慎!” 为什么呢?因为 WordPress 的帖子可不是孤零零的存在,它像一个蜈蚣,身上挂满了元数据、评论、附件等等“小弟”。你要是直接用 SQL 语句咔嚓一下删掉主贴,那些“小弟”可就成了孤魂野鬼,数据库里留下了一堆垃圾数据。

这时候,wp_delete_post() 就闪亮登场了!它就像一个专业的“清道夫”,不但能删掉主贴,还能把那些相关的“小弟”一并处理掉,保证数据库的干净整洁。

wp_delete_post() 的庐山真面目

让我们先睹为快,看看 wp_delete_post() 函数的源码(基于 WordPress 最新版本):

function wp_delete_post( $postid, $force_delete = false ) {
    global $wpdb;

    $postid = absint( $postid );
    if ( ! $postid ) {
        return false;
    }

    $post = get_post( $postid );

    if ( ! $post ) {
        return false;
    }

    $post_type_object = get_post_type_object( $post->post_type );

    if ( ! current_user_can( $post_type_object->cap->delete_post, $postid ) ) {
        return false;
    }

    if ( 'attachment' === $post->post_type ) {
        return wp_delete_attachment( $postid, $force_delete );
    }

    /**
     * Fires before a post is deleted, at the start of the 'wp_delete_post' function.
     *
     * @since 4.3.0
     *
     * @param int $postid Post ID.
     */
    do_action( 'wp_delete_post', $postid );

    if ( 'trash' === $post->post_status && ! $force_delete ) {
        return wp_untrash_post( $postid );
    }

    /**
     * Fires before trashing or deleting a post.
     *
     * @since 2.9.0
     *
     * @param int $postid Post ID.
     */
    do_action( 'before_delete_post', $postid );

    if ( $force_delete ) {
        /**
         * Fires before a post is permanently deleted.
         *
         * @since 3.2.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'delete_post', $postid );

        $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) );
        if ( ! $result ) {
            return false;
        }

        wp_delete_post_terms( $postid, get_object_taxonomies( $post ) );

        delete_post_meta( $postid );

        $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ) );
        foreach ( $comment_ids as $comment_id ) {
            wp_delete_comment( $comment_id, true );
        }

        wp_delete_post_revisions( $postid );

        clean_post_cache( $postid );

        /**
         * Fires after a post is permanently deleted.
         *
         * @since 2.2.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'after_delete_post', $postid );
    } else {
        wp_trash_post( $postid );
        return $post;
    }

    return $post;
}

怎么样,是不是感觉有点长?别怕,咱们一点一点来分析。

源码逐行解读

  1. 函数定义:

    function wp_delete_post( $postid, $force_delete = false ) {
    • wp_delete_post():函数名,不用多说。
    • $postid:要删除的帖子 ID,必须是整数。
    • $force_delete:是否强制删除(彻底删除)。默认为 false,表示放入回收站。如果设置为 true,则直接从数据库中删除。这个参数非常重要,关系到你的帖子是进“回收站”还是直接“火葬场”。
  2. 安全检查:

    global $wpdb;
    
    $postid = absint( $postid );
    if ( ! $postid ) {
        return false;
    }
    
    $post = get_post( $postid );
    
    if ( ! $post ) {
        return false;
    }
    
    $post_type_object = get_post_type_object( $post->post_type );
    
    if ( ! current_user_can( $post_type_object->cap->delete_post, $postid ) ) {
        return false;
    }
    • global $wpdb;:引入全局数据库对象,后面要用它来操作数据库。
    • $postid = absint( $postid );:把帖子 ID 转换成绝对整数,防止注入攻击。
    • if ( ! $postid ) { return false; }:如果帖子 ID 为空,直接返回 false,啥也不做。
    • $post = get_post( $postid );:根据帖子 ID 获取帖子对象。
    • if ( ! $post ) { return false; }:如果帖子不存在,直接返回 false
    • $post_type_object = get_post_type_object( $post->post_type );:获取帖子类型对象,比如 post、page、attachment 等。
    • if ( ! current_user_can( $post_type_object->cap->delete_post, $postid ) ) { return false; }:检查当前用户是否有权限删除该帖子。WordPress 的权限管理非常严格,不是谁想删就能删的。

    这一段代码主要是做一些基本的安全检查,确保要删除的帖子存在,并且当前用户有权限删除它。

  3. 附件特殊处理:

    if ( 'attachment' === $post->post_type ) {
        return wp_delete_attachment( $postid, $force_delete );
    }

    如果帖子类型是附件(attachment),则调用 wp_delete_attachment() 函数来删除附件。附件的处理方式和普通帖子略有不同,需要单独处理。

  4. 钩子(Hooks):

    /**
     * Fires before a post is deleted, at the start of the 'wp_delete_post' function.
     *
     * @since 4.3.0
     *
     * @param int $postid Post ID.
     */
    do_action( 'wp_delete_post', $postid );
    
    /**
     * Fires before trashing or deleting a post.
     *
     * @since 2.9.0
     *
     * @param int $postid Post ID.
     */
    do_action( 'before_delete_post', $postid );
    
    if ( $force_delete ) {
        /**
         * Fires before a post is permanently deleted.
         *
         * @since 3.2.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'delete_post', $postid );
    }

    WordPress 的钩子机制非常强大,允许开发者在特定事件发生时执行自定义代码。这里使用了三个钩子:

    • wp_delete_post:在 wp_delete_post() 函数开始时触发。
    • before_delete_post:在帖子被放入回收站或永久删除之前触发。
    • delete_post:在帖子被永久删除之前触发。

    通过这些钩子,你可以实现各种各样的功能,比如在删除帖子前发送邮件通知管理员,或者记录删除日志等等。

  5. 回收站 vs. 永久删除:

    if ( 'trash' === $post->post_status && ! $force_delete ) {
        return wp_untrash_post( $postid );
    }
    
    if ( $force_delete ) {
        // 永久删除的代码
    } else {
        wp_trash_post( $postid );
        return $post;
    }
    • 如果帖子的状态是 trash(回收站),并且 $force_deletefalse,则调用 wp_untrash_post() 函数将帖子从回收站中恢复。
    • 如果 $force_deletetrue,则执行永久删除的代码。
    • 否则,调用 wp_trash_post() 函数将帖子放入回收站。

    这里体现了 WordPress 的一个重要设计理念:先放入回收站,给用户一个后悔的机会。只有在用户明确表示要永久删除时,才会真正从数据库中删除。

  6. 永久删除的核心代码:

    if ( $force_delete ) {
        /**
         * Fires before a post is permanently deleted.
         *
         * @since 3.2.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'delete_post', $postid );
    
        $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) );
        if ( ! $result ) {
            return false;
        }
    
        wp_delete_post_terms( $postid, get_object_taxonomies( $post ) );
    
        delete_post_meta( $postid );
    
        $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ) );
        foreach ( $comment_ids as $comment_id ) {
            wp_delete_comment( $comment_id, true );
        }
    
        wp_delete_post_revisions( $postid );
    
        clean_post_cache( $postid );
    
        /**
         * Fires after a post is permanently deleted.
         *
         * @since 2.2.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'after_delete_post', $postid );
    }

    这部分代码是 wp_delete_post() 函数的核心,负责永久删除帖子以及相关的元数据、评论和修订版本。

    • $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) );:从 wp_posts 表中删除帖子。
    • wp_delete_post_terms( $postid, get_object_taxonomies( $post ) );:删除帖子相关的分类和标签(terms)。
    • delete_post_meta( $postid );:删除帖子相关的元数据(custom fields)。
    • $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ) );:获取帖子相关的评论 ID。
    • foreach ( $comment_ids as $comment_id ) { wp_delete_comment( $comment_id, true ); }:循环删除帖子相关的评论。注意,这里 $force_delete 设置为 true,表示永久删除评论。
    • wp_delete_post_revisions( $postid );:删除帖子的修订版本。
    • clean_post_cache( $postid );:清除帖子缓存,确保下次访问时获取的是最新的数据。

    这一段代码非常重要,它保证了删除帖子的彻底性,避免了数据库中留下垃圾数据。

  7. 最后的钩子:

    /**
     * Fires after a post is permanently deleted.
     *
     * @since 2.2.0
     *
     * @param int $postid Post ID.
     */
    do_action( 'after_delete_post', $postid );
    • after_delete_post:在帖子被永久删除之后触发。

    这个钩子可以用来执行一些清理工作,比如更新统计数据,或者发送删除成功的通知等等。

  8. 返回值:

    return $post;
    • 如果帖子被放入回收站,则返回帖子对象。
    • 如果帖子被永久删除,则返回帖子对象。注意,即使帖子已经被删除,仍然返回帖子对象,这可能是为了方便后续操作。

重点函数解析

  • wp_delete_post_terms()

    这个函数负责删除帖子相关的分类和标签(terms)。它会先获取帖子所有的分类和标签,然后循环调用 wp_remove_object_terms() 函数来删除它们。

    function wp_delete_post_terms( $post_id, $taxonomies ) {
        foreach ( (array) $taxonomies as $taxonomy ) {
            $terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) );
            wp_remove_object_terms( $post_id, $terms, $taxonomy );
        }
    }
  • delete_post_meta()

    这个函数负责删除帖子相关的元数据(custom fields)。它会根据帖子 ID 从 wp_postmeta 表中删除所有相关的记录。

    function delete_post_meta( $post_id ) {
        global $wpdb;
    
        $post_id = absint( $post_id );
        if ( ! $post_id ) {
            return false;
        }
    
        $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) );
    }
  • wp_delete_comment()

    这个函数负责删除评论。它可以删除单个评论,也可以删除所有与帖子相关的评论。

    function wp_delete_comment( $comment_id, $force_delete = false ) {
        global $wpdb;
    
        $comment_id = absint( $comment_id );
        if ( ! $comment_id ) {
            return false;
        }
    
        $comment = get_comment( $comment_id );
    
        if ( ! $comment ) {
            return false;
        }
    
        /**
         * Fires before a comment is deleted from the database.
         *
         * @since 2.7.0
         *
         * @param int     $comment_id The comment ID.
         * @param WP_Comment $comment   The comment object.
         */
        do_action( 'delete_comment', $comment_id, $comment );
    
        if ( $force_delete ) {
            /**
             * Fires immediately before a comment is permanently deleted from the database.
             *
             * @since 2.7.0
             *
             * @param int     $comment_id The comment ID.
             * @param WP_Comment $comment   The comment object.
             */
            do_action( 'before_delete_comment', $comment_id, $comment );
    
            $result = $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment_id ) );
            if ( ! $result ) {
                return false;
            }
    
            wp_delete_comment_meta_by_mid( $comment_id );
    
            /**
             * Fires immediately after a comment is permanently deleted from the database.
             *
             * @since 2.7.0
             *
             * @param int $comment_id The comment ID.
             */
            do_action( 'after_delete_comment', $comment_id );
        } else {
            wp_trash_comment( $comment_id );
        }
    
        /**
         * Fires after a comment has been deleted from the database.
         *
         * @since 2.7.0
         *
         * @param int $comment_id The comment ID.
         */
        do_action( 'deleted_comment', $comment_id );
    
        return true;
    }
  • wp_delete_post_revisions()

    这个函数负责删除帖子的修订版本。修订版本是 WordPress 自动保存的帖子历史版本,可以用来恢复之前的版本。

    function wp_delete_post_revisions( $post_id ) {
        global $wpdb;
    
        $post_id = absint( $post_id );
        if ( ! $post_id ) {
            return false;
        }
    
        $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $post_id ) );
    
        if ( $revision_ids ) {
            foreach ( $revision_ids as $revision_id ) {
                wp_delete_post( $revision_id, true );
            }
        }
    }

使用场景举例

  1. 从后台删除帖子:

    当你从 WordPress 后台删除一个帖子时,WordPress 会自动调用 wp_delete_post() 函数。默认情况下,帖子会被放入回收站。如果你想永久删除帖子,可以在回收站中选择“永久删除”。

  2. 自定义代码删除帖子:

    你也可以在自定义代码中调用 wp_delete_post() 函数来删除帖子。例如:

    $post_id = 123; // 要删除的帖子 ID
    $force_delete = true; // 永久删除
    
    $result = wp_delete_post( $post_id, $force_delete );
    
    if ( $result ) {
        echo '帖子删除成功!';
    } else {
        echo '帖子删除失败!';
    }
  3. 插件开发:

    如果你正在开发一个 WordPress 插件,可能需要删除帖子。wp_delete_post() 函数是你的好帮手。

注意事项

  • 权限问题: 确保当前用户有权限删除帖子。
  • $force_delete 参数: 谨慎使用 $force_delete 参数。一旦设置为 true,帖子将被永久删除,无法恢复。
  • 钩子: 充分利用 WordPress 的钩子机制,可以在删除帖子前后执行自定义代码。

总结

wp_delete_post() 函数是 WordPress 中一个非常重要的函数,它负责删除帖子以及相关的元数据、评论和修订版本。通过分析源码,我们可以深入了解 WordPress 的工作原理,并更好地使用它。记住,删帖需谨慎,请三思而后行!

结尾

今天就到这里了,希望大家对 wp_delete_post() 函数有了更深入的了解。下次再遇到删帖的问题,就不用再抓瞎了。记住,阿码永远是你最可靠的“代码向导”! 咱们下期再见! 祝大家代码无 Bug,生活更精彩!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注