wp_delete_attachment()WP 2.0.0

Deletes a WordPress attachment (media library file, attached file). The file is physically deleted.

This function does the same thing as if you went to the Media Library and deleted an image (attachment).

This function does not check if the deleted attachments are used on the site (somewhere in the content). So, if you delete an attachment and it is used in the content of a post, the link to this image will become broken in the content of the post.

Deletes:

  • The post from the wp_posts table.
  • Related metadata.
  • Taxonomy relationships (categories, tags, custom taxonomies) (if any).
  • Related comments (if any).
  • Files related to the attachment (original, thumbnails).

Returns

WP_Post|false|null. false on failed deletion. Data of the attachment upon deletion.

The check should be performed using the comparison operator ===, not ==, because the function may return 0 or an empty array in case of successful deletion.

Usage

wp_delete_attachment( $attachment_id, $force_delete );
$attachment_id(number) (required)
ID of the attachment you want to delete.
$force_delete(boolean)

true - permanent deletion bypassing the trash, if the trash is enabled.

When $force_delete = false - attachments are moved to the trash, although by default there is no button in the media library to access the trash - this can be confusing. Also, attachment files (physical files in wp-content/uploads) are kept as is.

When $force_delete = true - attachment files (physical files in wp-content/uploads) will be deleted.

Default: false

Examples

0

#1 Deleting an attachment

We will irrevocably delete the attachment with ID 54:

wp_delete_attachment( 54, true );
0

#2 Delete all post attachments (attached files), along with the deletion of the post

On some blogs it is convenient to make it so that when you delete a post, all the media files attached to it would be deleted along with it. You can do it this way:

// Deletes all post attachments (attached media files) along with the post(s)
add_action( 'before_delete_post', 'wpkama_delete_attaches_with_post' );
function wpkama_delete_attaches_with_post( $post_id ) {
	$post = get_post( $post_id );

	$post_types = [ 'article', 'question' ]; // delete attachments only for these post types

	if( ! $post || ! in_array( $post->post_type, $post_types ) ){
		return;
	}

	$attaches = get_children( [
		'post_type'   => 'attachment',
		'post_parent' => $post_id,
	] );

	if( ! $attaches ){
		return;
	}

	foreach( $attaches as $attach ){
		wp_delete_attachment( $attach->ID, true );
	}
}

The hook before_delete_post, because when the post is deleted, all attachments take the status unattached, i.e. the post_parent value is deleted, and the post attachments are selected by it. So the hooks delete_post and after_delete_post will not work.

0

#3 Deleting a media file with verification

Let's delete the attachment and check if the specified media file was really deleted:

if( false === wp_delete_attachment( 54, true ) ){
	 echo "Failed to delete media file";
} else {
	 echo "The media file has been deleted";
}

Notes

  • Global. wpdb. $wpdb WordPress database abstraction object.

Changelog

Since 2.0.0 Introduced.

wp_delete_attachment() code WP 6.9.1

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

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

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

	$post = get_post( $post );

	if ( 'attachment' !== $post->post_type ) {
		return false;
	}

	if ( ! $force_delete && EMPTY_TRASH_DAYS && MEDIA_TRASH && 'trash' !== $post->post_status ) {
		return wp_trash_post( $post_id );
	}

	/**
	 * Filters whether an attachment deletion should take place.
	 *
	 * @since 5.5.0
	 *
	 * @param WP_Post|false|null $delete       Whether to go forward with deletion.
	 * @param WP_Post            $post         Post object.
	 * @param bool               $force_delete Whether to bypass the Trash.
	 */
	$check = apply_filters( 'pre_delete_attachment', null, $post, $force_delete );
	if ( null !== $check ) {
		return $check;
	}

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

	$meta         = wp_get_attachment_metadata( $post_id );
	$backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
	$file         = get_attached_file( $post_id );

	if ( is_multisite() && is_string( $file ) && ! empty( $file ) ) {
		clean_dirsize_cache( $file );
	}

	/**
	 * Fires before an attachment is deleted, at the start of wp_delete_attachment().
	 *
	 * @since 2.0.0
	 * @since 5.5.0 Added the `$post` parameter.
	 *
	 * @param int     $post_id Attachment ID.
	 * @param WP_Post $post    Post object.
	 */
	do_action( 'delete_attachment', $post_id, $post );

	wp_delete_object_term_relationships( $post_id, array( 'category', 'post_tag' ) );
	wp_delete_object_term_relationships( $post_id, get_object_taxonomies( $post->post_type ) );

	// Delete all for any posts.
	delete_metadata( 'post', null, '_thumbnail_id', $post_id, true );

	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 );
	}

	/** This action is documented in wp-includes/post.php */
	do_action( 'delete_post', $post_id, $post );
	$result = $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) );
	if ( ! $result ) {
		return false;
	}
	/** This action is documented in wp-includes/post.php */
	do_action( 'deleted_post', $post_id, $post );

	wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file );

	clean_post_cache( $post );

	return $post;
}