For some time I’ve noticed one thing that many people like doing. Recreating core functions. One being the_post_thumbnail(). The reason it drives me crazy is because it means maintaining more code in your theme. Why would you do that to yourself? One of the common things is to wrap it in a conditional check and then use the function if there is a thumbnail. Great! Looks like:

if ( has_post_thumbnail() ){
	the_post_thumbnail();
} else {
	custom_post_thumbnail();
}

The problem

Rewriting a core template tag to suit the theme’s design. Yes, it drives me insane when I know that there is a filter or a hook that can be applied to core functions. Yes. Core has a lot of hook and filters to modify. I’m not saying learn them all but merely suggesting look at the core code when you want to see what and how things are done.

Core file

In order to create our little function let us look at the actual core code, shall we? All the workings are actually placed in the wp-includes/post-thumbnail-template.php file. A little crazy, right? Let’s take a look at the code:

function the_post_thumbnail( $size = 'post-thumbnail', $attr = '' ) {
	echo get_the_post_thumbnail( null, $size, $attr );
}

There it is. So what does it do? It creates a default size ( post-thumbnail ) and passes a null argument to get_the_post_thumbnail. A little weird but makes sense, right? So let’s look at the actual function that gets us the image, yeah?

function get_the_post_thumbnail( $post_id = null, $size = 'post-thumbnail', $attr = '' ) {
	$post_id = ( null === $post_id ) ? get_the_ID() : $post_id;
	$post_thumbnail_id = get_post_thumbnail_id( $post_id );

	$size = apply_filters( 'post_thumbnail_size', $size );

	if ( $post_thumbnail_id ) {

		do_action( 'begin_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );
		if ( in_the_loop() )
			update_post_thumbnail_cache();
		$html = wp_get_attachment_image( $post_thumbnail_id, $size, false, $attr );

		do_action( 'end_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );

	} else {
		$html = '';
	}

	return apply_filters( 'post_thumbnail_html', $html, $post_id, $post_thumbnail_id, $size, $attr );
}

As you can see it looks like quite a bit. So we will break it down a little bit more. Go down the lines and explain a little more.

Breakdown: the arguments

The first thing you will see is that it asks for a $post_id, then an image size and an attribute. Cool. Still with me? Good. Now let’s move on to the next line of code:

$post_id = ( null === $post_id ) ? get_the_ID() : $post_id;

It tests if the $post_id is identical to null and if it is, we get the post’s ID by using get_the_ID(). Pretty neat if you ask me.

Next, it passes that ID to get_post_thumbnail_id in order to get the image’s attachment ID. You read it right.

The next line is where we come across our first filter. Yay! And it is post_thumbnail_size. What this means is that if you wanted to use

the_post_thumbnail( 'my-theme-image' );

You can add a filter and make it a conditional setting as well. You can use:

add_filter( 'post_thumbnail_size', 'my_new_sizes' );
function my_new_sizes( $size ){
	if ( is_front_page() || is_home() ){
		return 'my-large-front-image';
	}
	return 'my-theme-image';
}

Awesome, right? I like to think so. The next several lines are what create the actual code.

First it checks if it is a true value and if it is we can continue with our code. Then we see an interesting line:

do_action( 'begin_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );

Kind of a neat little hook. You can print things just before the image tag. From there it updates the thumbnail cache, and then finally it gets the image’s markup by using wp_get_attachment_image( $post_thumbnail_id, $size, false, $attr ). Cool, right? Right. But we are not done just yet.

We have another hook:

do_action( 'end_fetch_post_thumbnail_html', $post_id, $post_thumbnail_id, $size );

With this particular one you can also print things out before the image’s markup. For example:

add_action( 'end_fetch_post_thumbnail_html', 'demo_before_image' );
function demo_before_image(){
	_e( 'I appear before the image', 'textdomain' );
}

The end result will be:

I appear before the image<img width="522" height="270" src="https://sample.dev/wp-content/uploads/2013/03/featured-image-horizontal.jpg">;

Super cool, right?

The next and final steps in the function is the setting of $html. You’ll see that if the $post_thumbnail_id isn’t set then it returns an empty string. From there the last line of code is:

return apply_filters( 'post_thumbnail_html', $html, $post_id, $post_thumbnail_id, $size, $attr );

So, it returns the HTML. Great. As you can see you can filter this.

New filter

Yes, we will use a filter because that is what all good developers like to use and should use.

add_filter( 'post_thumbnail_html', 'theme_slug_post_thumbnail', 10, 2 );
function theme_slug_post_thumbnail( $html, $size ) {
	// get featured image based on $post->ID
	$post_thumbnail_id = get_post_thumbnail_id( get_the_ID() );

	// set size
	$size = (string) apply_filters( 'post_thumbnail_size', 'my-theme-image' );

	//do your magic here
	if ( $post_thumbnail_id ) {
		if ( in_the_loop() ) {
			update_post_thumbnail_cache();
		}
		$html = wp_get_attachment_image( $post_thumbnail_id, $size );
	} else {
		$html = '<img class="featured-image" src="' . get_template_directory_uri() . '/img/custom-thumb.jpg" alt="Image">';
	}

	return '<div class="feature-image-wrapper">' . $html . '</div>';
}

As you can see it is a simplified filter because we aren’t changing every variable the function has to offer. You can, of course, explore more and create your own version. You will see that the one filter I kept was the post_thumbnail_size and that is because if a child theme is created, the child theme can change that with a simple filter.