I don't even know where to start with figuring this out....

This function for functions.php grabs the first attached image in a post and sets it as the featured image. The problem is that it will not attach an image if it is already attached to another post. I've tested this a number of times.

How can this function be changed to allow an image that is already attached to another post be set as the featured image on a new post?

    function wpse127196_auto_set_featured_image() {
              global $post;
              $featured_image_exists = has_post_thumbnail($post->ID);
                  if (!$featured_image_exists)  {
                  $attached_image = get_children(
"post_parent=$post->ID&post_type=attachment&post_mime_type=image&numberposts=1" );
           if ($attached_image) {
                  foreach ($attached_image as $attachment_id => $attachment) {
                                set_post_thumbnail($post->ID, $attachment_id);
    add_action('save_post', 'wpse127196_auto_set_featured_image');
    add_action('draft_to_publish', 'wpse127196_auto_set_featured_image');
    add_action('new_to_publish', 'wpse127196_auto_set_featured_image');
    add_action('pending_to_publish', 'wpse127196_auto_set_featured_image');
    add_action('future_to_publish', 'wpse127196_auto_set_featured_image');

First of all, I can't understand why you need to hook 5 different actions: save_post is enough: it is called on post publish and update.

After that the function you posted dont get the image in the post content, but take first image that are child of the current post.

An image is a child of a post when:

  • it is uploaded from the post edit screen (via "Add Media" button, above the post content editor)
  • it is uploaded from the media page, and for first time it is inserted in a post

However relationship between posts and media is one to many: a post can have many media children, but a media can have only one parent.

This is the reason why if an image is already attached to a post (it is a child of the post) your function can't work because that image can't be child of 2 (or more) posts.

So, if you want to get first image in post content, you should look at post content and extract first image, then from the image take the image id, and finally use that id as post thumbnail.

add_action('save_post', 'wpse127196_auto_set_featured_image', 10, 2);

function wpse127196_auto_set_featured_image( $postid, $post ) {
  // do work only if there is no featured image already
  // and no need to rely on global post: the function receive
  // post id and post object as arguments
  if ( ! has_post_thumbnail($postid) ) {
     // get the url of first image in post content
     $image = wpse127196_get_first_image( $post->post_content);
     if ( ! empty($image) ) {
        // get the image id from the url
        $img_id =  wpse127196_get_img_id_from_url( $image );
        if ( (int) $img_id > 0 ) {
          // finally set post thumbnail
          set_post_thumbnail($postid, $img_id );

As you can see, there are 2 functions used in the function above: wpse127196_get_first_image and wpse127196_get_img_id_from_url.

The first, that extract and image from the content, can be wrote in different way. Probably the most solid is to use an html parser (Google for it). A simpler but less affordable way is use a regex.

For sake of simplicity I'll use regex here:

function wpse127196_get_first_image( $text = '' ) {
  if ( is_string($text) && ! empty($text) ) {
    $m = array();
    preg_match_all('#src=["\']([^"\']*\.jpg|jpeg|gif|png)["\']#i' , $text, $m);
    if ( isset($m[1]) && ! empty($m[1]) ) {
       $path = wp_upload_dir();
       foreach( $m[1] as $url ) {
         if ( false !== strpos( $url, $path['baseurl'] ) ) { // skip external images
           return $url;

The function that retrieve the id starting from url, comes from here

function wpse127196_get_img_id_from_url( $url ) {
  $id = 0;
  if ( filter_var($url, FILTER_VALIDATE_URL) ) {
    $upload_dir_paths = wp_upload_dir();
    // skip external images
    if ( false !== strpos( $url, $upload_dir_paths['baseurl'] ) ) {
      global $wpdb;
      // If this is the URL of an auto-generated thumbnail
      // get the URL of the original image
      $url = preg_replace( '/-\d+x\d+(?=\.(jpg|jpeg|png|gif)$)/i', '', $url );
      // Remove the upload path base directory from the attachment URL
      $url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $url );
      // Finally, run a custom database query to get the attachment ID from URL
      $id = $wpdb->get_var( $wpdb->prepare(
        "SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta 
        WHERE wposts.ID = wpostmeta.post_id 
        AND wpostmeta.meta_key = '_wp_attached_file' 
        AND wpostmeta.meta_value = '%s' 
        AND wposts.post_type = 'attachment'", $url
      ) );
  return $id;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.