函数文档

wp_delete_post()

💡 云策文档标注

概述

wp_delete_post() 是 WordPress 核心函数,用于将文章或页面移至回收站或永久删除。当永久删除时,会连带删除所有关联数据,如评论、元数据和分类术语。默认情况下,文章或页面会移至回收站,除非回收站被禁用、项目已在回收站中,或 $force_delete 参数设为 true。

关键要点

  • 函数接受两个参数:$post_id(必需,文章 ID)和 $force_delete(可选,布尔值,默认为 false,决定是否绕过回收站强制删除)。
  • 返回值:成功时返回 WP_Post 对象,失败时返回 false 或 null。
  • 对于文章或页面类型,若 $force_delete 为 false、回收站功能启用且文章不在回收站中,函数会自动调用 wp_trash_post()。
  • 对于附件类型,会调用 wp_delete_attachment() 处理。
  • 删除过程会触发多个钩子,如 pre_delete_post、before_delete_post、delete_post、deleted_post 等,允许开发者干预或执行额外操作。
  • 永久删除时,会清理相关数据,包括评论、元数据、分类关系、修订版本和子页面链接。
  • 注意:自定义文章类型默认会被永久删除,不受 $force_delete 参数影响,如需移至回收站应使用 wp_trash_post()。

代码示例

// 删除 ID 为 1 的文章,默认移至回收站
wp_delete_post( 1 );

// 强制永久删除 ID 为 2 的文章
wp_delete_post( 2, true );

// 删除所有自定义文章类型“products”的文章
function wpdocs_delete_all_products() {
    $myproducts = get_pages( array( 'post_type' => 'products') );
    foreach ( $myproducts as $myproduct ) {
        wp_delete_post( $myproduct->ID, true ); // true 为永久删除,false 则移至回收站
    }
}
add_action( 'init', 'wpdocs_delete_all_products' );

注意事项

  • 使用 $force_delete 参数时需谨慎,因为永久删除不可恢复。
  • 对于自定义文章类型,wp_delete_post() 默认永久删除,不遵循回收站逻辑,开发者应显式处理回收站需求。
  • 函数内部涉及数据库操作,建议在适当钩子或权限检查后调用,以避免数据不一致。

📄 原文内容

Trashes or deletes a post or page.

Description

When the post and page is permanently deleted, everything that is tied to it is deleted also. This includes comments, post meta fields, and terms associated with the post.

The post or page is moved to Trash instead of permanently deleted unless Trash is disabled, item is already in the Trash, or $force_delete is true.

See also

Parameters

$post_idintrequired
Post ID. (The default of 0 is for historical reasons; providing it is incorrect.)
$force_deletebooloptional
Whether to bypass Trash and force deletion.

Default:false

Return

WP_Post|false|null Post data on success, false or null on failure.

More Information

wp_delete_post() automatically reverts to wp_trash_post() if $force_delete is false, the post_type of $postid is page or post, $postid is not already in the trash and if that trash feature enabled (which it it is by default).

Source

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

	$post_id = (int) $post_id;
	if ( $post_id <= 0 ) {
		_doing_it_wrong( __FUNCTION__, __( 'The post ID must be greater than 0.' ), '6.9.0' );
		return false;
	}

	$post = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id ) );

	if ( ! $post ) {
		return $post;
	}

	$post = get_post( $post );

	if ( ! $force_delete
		&& ( 'post' === $post->post_type || 'page' === $post->post_type )
		&& 'trash' !== get_post_status( $post_id ) && EMPTY_TRASH_DAYS
	) {
		return wp_trash_post( $post_id );
	}

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

	/**
	 * Filters whether a post deletion should take place.
	 *
	 * @since 4.4.0
	 *
	 * @param WP_Post|false|null $check        Whether to go forward with deletion. Anything other than null will short-circuit deletion.
	 * @param WP_Post            $post         Post object.
	 * @param bool               $force_delete Whether to bypass the Trash.
	 */
	$check = apply_filters( 'pre_delete_post', null, $post, $force_delete );
	if ( null !== $check ) {
		return $check;
	}

	/**
	 * Fires before a post is deleted, at the start of wp_delete_post().
	 *
	 * @since 3.2.0
	 * @since 5.5.0 Added the `$post` parameter.
	 *
	 * @see wp_delete_post()
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( 'before_delete_post', $post_id, $post );

	delete_post_meta( $post_id, '_wp_trash_meta_status' );
	delete_post_meta( $post_id, '_wp_trash_meta_time' );

	wp_delete_object_term_relationships( $post_id, get_object_taxonomies( $post->post_type ) );

	$parent_data  = array( 'post_parent' => $post->post_parent );
	$parent_where = array( 'post_parent' => $post_id );

	if ( is_post_type_hierarchical( $post->post_type ) ) {
		// Point children of this page to its parent, also clean the cache of affected children.
		$children_query = $wpdb->prepare(
			"SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s",
			$post_id,
			$post->post_type
		);

		$children = $wpdb->get_results( $children_query );

		if ( $children ) {
			$wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) );
		}
	}

	// Do raw query. wp_get_post_revisions() is filtered.
	$revision_ids = $wpdb->get_col(
		$wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $post_id )
	);

	// Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up.
	foreach ( $revision_ids as $revision_id ) {
		wp_delete_post_revision( $revision_id );
	}

	// Point all attachments to this post up one level.
	$wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );

	wp_defer_comment_counting( true );

	$comment_ids = $wpdb->get_col(
		$wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d ORDER BY comment_ID DESC", $post_id )
	);

	foreach ( $comment_ids as $comment_id ) {
		wp_delete_comment( $comment_id, true );
	}

	wp_defer_comment_counting( false );

	$post_meta_ids = $wpdb->get_col(
		$wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $post_id )
	);

	foreach ( $post_meta_ids as $mid ) {
		delete_metadata_by_mid( 'post', $mid );
	}

	/**
	 * Fires immediately before a post is deleted from the database.
	 *
	 * The dynamic portion of the hook name, `$post->post_type`, refers to
	 * the post type slug.
	 *
	 * @since 6.6.0
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( "delete_post_{$post->post_type}", $post_id, $post );

	/**
	 * Fires immediately before a post is deleted from the database.
	 *
	 * @since 1.2.0
	 * @since 5.5.0 Added the `$post` parameter.
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( 'delete_post', $post_id, $post );

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

	/**
	 * Fires immediately after a post is deleted from the database.
	 *
	 * The dynamic portion of the hook name, `$post->post_type`, refers to
	 * the post type slug.
	 *
	 * @since 6.6.0
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( "deleted_post_{$post->post_type}", $post_id, $post );

	/**
	 * Fires immediately after a post is deleted from the database.
	 *
	 * @since 2.2.0
	 * @since 5.5.0 Added the `$post` parameter.
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( 'deleted_post', $post_id, $post );

	clean_post_cache( $post );

	if ( is_post_type_hierarchical( $post->post_type ) && $children ) {
		foreach ( $children as $child ) {
			clean_post_cache( $child );
		}
	}

	wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) );

	/**
	 * Fires after a post is deleted, at the conclusion of wp_delete_post().
	 *
	 * @since 3.2.0
	 * @since 5.5.0 Added the `$post` parameter.
	 *
	 * @see wp_delete_post()
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( 'after_delete_post', $post_id, $post );

	return $post;
}

Hooks

do_action( ‘after_delete_post’, int $post_id, WP_Post $post )

Fires after a post is deleted, at the conclusion of wp_delete_post() .

do_action( ‘before_delete_post’, int $post_id, WP_Post $post )

Fires before a post is deleted, at the start of wp_delete_post() .

do_action( ‘deleted_post’, int $post_id, WP_Post $post )

Fires immediately after a post is deleted from the database.

do_action( “deleted_post_{$post->post_type}”, int $post_id, WP_Post $post )

Fires immediately after a post is deleted from the database.

do_action( ‘delete_post’, int $post_id, WP_Post $post )

Fires immediately before a post is deleted from the database.

do_action( “delete_post_{$post->post_type}”, int $post_id, WP_Post $post )

Fires immediately before a post is deleted from the database.

apply_filters( ‘pre_delete_post’, WP_Post|false|null $check, WP_Post $post, bool $force_delete )

Filters whether a post deletion should take place.

Changelog

Version Description
1.0.0 Introduced.

User Contributed Notes

  1. Skip to note 6 content

    /**
     * Deletes all posts from "products" custom post type.
     */
    function wpdocs_delete_all_products() {
        $myproducts = get_pages( array( 'post_type' => 'products') );
    
        foreach ( $myproducts as $myproduct ) {
            // Delete all products.
            wp_delete_post( $myproduct->ID, true); // Set to False if you want to send them to Trash.
        } 
    }
    add_action( 'init', 'wpdocs_delete_all_products' );