Skip to content

Instantly share code, notes, and snippets.

@filipecsweb
Last active April 10, 2023 23:39
Show Gist options
  • Save filipecsweb/1dc6f5672261c33e4b4d5205816583c8 to your computer and use it in GitHub Desktop.
Save filipecsweb/1dc6f5672261c33e4b4d5205816583c8 to your computer and use it in GitHub Desktop.
WordPress - Delete unused image files from uploads directory
<?php
/**
* @author Filipe Seabra
* @link https://wp-cli.org/
* @version 1.0.0
*
* Put this file in the root directory of your WordPress installation.
*
* You make better usage of this file by using WP CLI (link above). Command line example:
* `wp eval-file wp-sweep-uploads.php`.
*/
/**
* In case you are not using WP CLI to execute this file uncomment the line below.
* NOT RECOMMENDED.
*/
//require_once 'wp-load.php'; // Assumes this file is located within WordPress root installation folder.
// Keeps the full path to your WordPress installation uploads folder.
$uploads_dir = wp_get_upload_dir()['basedir'];
// Keeps all occurrences found within the uploads/ folder.
$years = array_filter(
array_diff(
scandir( $uploads_dir ),
/**
* Add to this list any other directory/file name you want to exclude from the scan.
*/
array(
'.',
'..',
'index.php',
'wc-logs',
'woocommerce_uploads',
)
),
function ( $v ) use ( $uploads_dir ) {
if ( ! is_dir( $uploads_dir . "/$v" ) ) {
return false;
}
return true;
}
);
foreach ( $years as $year ) {
// Keeps all occurrences found within the uploads/{year}/ folder.
$months = array_filter(
array_diff(
scandir( $uploads_dir . "/$year" ),
/**
* Add to this list any other directory/file name you want to exclude from the scan.
*/
array(
'.',
'..',
'index.php',
)
),
function ( $v ) use ( $uploads_dir, $year ) {
if ( ! is_dir( $uploads_dir . "/$year/$v" ) ) {
return false;
}
return true;
}
);
foreach ( $months as $month ) {
// Keeps all occurrences found within the uploads/{year}/{month}/ folder.
$images = array_filter(
array_diff(
scandir( $uploads_dir . "/$year/$month" ),
/**
* Add to this list any other directory/file name you want to exclude from the scan.
*/
array(
'.',
'..',
'index.php',
)
),
function ( $v ) use ( $uploads_dir, $year, $month ) {
if ( is_dir( $uploads_dir . "/$year/$month/$v" ) ) {
return false;
}
return true;
}
);
/**
* Notice that in order to use $wpdb you have to load WordPress core.
*
* @global $wpdb
*/
global $wpdb;
foreach ( $images as $image ) {
$query = "SELECT post_id, meta_key, meta_value FROM ";
$query .= $wpdb->prefix . "postmeta WHERE meta_key = '_wp_attachment_metadata' ";
$query .= "AND meta_value LIKE '%$image%' LIMIT 1";
$r = $wpdb->get_results( $query );
if ( empty( $r[0] ) ) {
// DELETES THE IMAGE FILE THAT IS NOT BEING USED BY THE WORDPRESS MEDIA LIBRARY!
unlink( $uploads_dir . "/$year/$month/$image" );
}
}
}
}
@blenkhn
Copy link

blenkhn commented Apr 10, 2023

Hi,

I get the following errors from running the script, am I doing something wrong? it seems to run anyway.

I would also like the script to test for inline images as well. Images that are in a text editor as compared to an attachment.

Notice: Function WP_User_Query::query was called incorrectly. User queries should not be run before the plugins_loaded hook. Please see Debugging in WordPress for more information. (This message was added in version 6.1.1.) in /home/keyinnov/public_html/wp-includes/functions.php on line 5865
Notice: Function register_rest_route was called incorrectly. The REST API route definition for w1/v1/branding_method is missingthe required permission_callback argument. For REST API routes that are intended to be public, use __return_true as the permission callback. Please see Debugging in WordPress for more information. (This message was added in version 5.5.0.) in /home/keyinnov/public_html/wp-includes/functions.php on line 5865

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment