The situation
WordPress stores image/media details as an attachment
port type within the database. When you edit an image in the media library, all the details are correctly saved in the database. We can take most of these details from the wp_posts
table, with the exception of alt
text which is stored in the wp_postmeta
separately.
Regarding images that have been added previously to a post, the image details are included as an HTML markup (an img
tag with arguments) in the post content. It seems like, there is a loose connection/control from the media side to the post content. I presume that WordPress developers attempted to conserve server resources by reducing media functions while authors have full control when they edit a post.
The implementation
So, we need to implement a method to meet your requirements, and I hope the following PHP function will handle most of it. The function has the following parts:
#1. Gather posts
#2. Find attachments that linked to a post. (You can change this logic for your needs.)
#3. Find the appropriate markup by a class name from the content (.wp-image-AttachID
)
#4. Replace image details (alt, title, and caption) with regex patterns
#5. Update post content
function image_meta_update_in_post_content($per_page = -1, $paged = 0) {
// #1. Get posts
$the_query = new WP_Query(array(
'posts_per_page' => $per_page,
'paged' => $paged,
'post_type' => 'post',
'post_status' => 'any'
));
$log = '';
if ($the_query->have_posts()) {
while ($the_query->have_posts()) {
$the_query->the_post();
// #2. Find post attachments | You can change this logic
$args = array(
'post_parent' => get_the_ID(),
'post_type' => 'attachment',
'post_mime_type' => 'image',
'posts_per_page' => -1,
);
$attachments = get_children($args);
if ($attachments) {
$content = get_the_content();
foreach ($attachments as $key => $attach) {
// #3. Find an attachment markup by ID from post content
$pattern = "~(<img(.*class=['\"](.*)wp-image-" . $attach->ID . "(.*)['\"].*)*>)+~";
if (preg_match($pattern, $content, $matches)) {
// Meta details: Alt, Caption and Image title
$alt = get_post_meta($attach->ID, '_wp_attachment_image_alt', true);
$caption = get_the_excerpt($attach->ID);
$title = get_the_title($attach->ID);
// #4. Replace meta
$patterns = [
'~(<img.*? alt=")(.*?)("[^>]*>)~i',
'~(<img.*? title=")(.*?)(")~i',
// '~\[caption\](.*?)\[\/caption\]~is' // To be updated
];
$replacements = [
"$1$alt$3",
"$1$title$3",
// "[caption]$1$caption$3[/caption]" // To be updated
];
$new_attachment = preg_replace($patterns, $replacements, $matches[0]);
$content = str_replace($matches[0], $new_attachment, $content);
$log .= "Has been updated (<b>Post: " . get_the_ID() . "</b>".
" | Image: $attach->ID, Title: $title, Alt : $alt, Caption: $caption)<br>";
}
}
// #5. Update post content
wp_update_post(array('ID' => get_the_ID(), 'post_content' => $content), true, true);
}
}
}
wp_reset_postdata();
echo $log;
}
Note 0
: Running through 2,000 posts at once could be challenging, so I've added an option to paginate them.
Note 1
: Regex patterns are mostly for the img tags and I hope it will work fine with classic post content or blocks editor. However, it is inevitable to make changes if you are using other content builders.
Note 2
: I recommend checking the post and attachment linking, or you may consider changing the logic.
The execution
The function may only need to run once or very infrequent. So it seems like there we don't need to add it to any theme or plugin.
[Completely optional]: If I were you, I would call it as a simple PHP script that could locate in the WP root directory. For instance, wp_root/image-meta-updater.php
Then import the wp-load.php
to enable all the WP functionality for the script.
ini_set('memory_limit', '256M');
set_time_limit(600);
require_once("wp-load.php");
image_meta_update_in_post_content(10, 1);
Calling
Note 3
: Don't forget to delete the file after a successful run or disallow it from public access.
Precaution
As the function is written roughly (especially regex) and saves modification directly to the database, please be cautious to avoid losing your content and follow the next steps:
Backup
your database
- Try the script on a
Staging
or Clone
server
- Or try to run it as a
Dry
like disabling wp_update_post
part
- Run with a few posts (5-10) and make sure