Skip to content

Instantly share code, notes, and snippets.

@pbiron
Last active August 16, 2018 14:30
Show Gist options
  • Save pbiron/d72a5d3b63e7077df767735464b2769c to your computer and use it in GitHub Desktop.
Save pbiron/d72a5d3b63e7077df767735464b2769c to your computer and use it in GitHub Desktop.
func to hook into WP's attachment_url_to_postid filter to try to resolve attachment URLs for intermediate/backup sizes
<?php
if ( version_compare( get_bloginfo( 'version' ), '4.2', '>=') ) :
/*
* WP 4.0 introduced attachment_url_to_postid(). However, it fails when the
* URL is for an intermediate sized image (e.g., `.../filename-150x150.jpg`).
*
* WP 4.2.0 introduced the attachment_url_to_postid filter. The filter func
* defined below hooks into that filter and attempts remedy that limitation.
*/
add_filter( 'attachment_url_to_postid' , 'shc_attachment_url_to_postid_filter', 10, 2 );
/**
* Tries to convert an attachment URL (for intermediate/edited sized image) into a post ID.
*
* @param string $url The URL to resolve.
* @return int The found post ID, or 0 on failure.
*
* @see attachment_url_to_postid()
*
* @todo produces incorrect results after the following sequence:
* 1) set thumbnail site to 150x150;
* 2) upload foo-150x150.jpg;
* 3) upload foo.jpg;
* 4) call attachment_url_to_post_id( 'http://host/wp-content/uploads/foo-150x150.jpg' )
*
* this is due to a bug in WP Core,
* see @link https://core.trac.wordpress.org/ticket/44095
*
* Actually, it is attachment_url_to_postid() that produces the incorrect
* results, and we don't try to work around that bug
*
* @todo produces incorrect results after the following sequence:
* 1) set thumbnail site to 150x150;
* 2) upload a 300x300 image foo.jpg;
* 3) edit foo.jpg and scale to 200x200;
* 4) regenerate intermediate sized images (e.g., with https://wordpress.org/plugins/regenerate-thumbnails/)
* 5) call attachment_url_to_post_id( 'http://host/wp-content/uploads/foo-150x150.jpg' )
*
* this is due to what may/may not be considered a bug in WP core, but the fix/enhancement suggested in
* @link https://core.trac.wordpress.org/ticket/44127 would allow a slight mod to this filter
* to correctly resolve that case
*/
function shc_attachment_url_to_postid_filter( $post_id, $url ) {
global $wpdb;
if ( $post_id ) {
// attachment_url_to_postid() found the attachment, nothing for us to do
// however, see the 1st @todo above for one know case where attachment_url_to_postid()
// returns a false positive. We could try to work around that bug, but I'd
// rather the bug be fixed rather than try to work around it
return $post_id;
}
// start by setting up a few vars the same way attachment_url_to_postid() does
$dir = wp_get_upload_dir();
$path = $url;
$site_url = parse_url( $dir['url'] );
$image_path = parse_url( $path );
//force the protocols to match if needed
if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
$path = str_replace( $image_path['scheme'], $site_url['scheme'], $path );
}
if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) {
$path = substr( $path, strlen( $dir['baseurl'] . '/' ) );
}
$basename = wp_basename( $path );
$dirname = dirname( $path );
// note: the "LIKE" we search for is the serialized form of $basename to reduce the
// number of false positives we have to deal with
$sql = $wpdb->prepare(
"SELECT post_id, meta_key, meta_value
FROM $wpdb->postmeta
WHERE meta_key IN ( '_wp_attachment_metadata', '_wp_attachment_backup_sizes' ) AND meta_value LIKE %s",
'%' . serialize( $basename ) . '%' );
$results = $wpdb->get_results( $sql );
foreach ( $results as $row ) {
if ( '_wp_attachment_metadata' === $row->meta_key ) {
$meta = maybe_unserialize( $row->meta_value );
if ( dirname( $meta['file'] ) === $dirname && in_array( $basename, wp_list_pluck( $meta['sizes'], 'file' ) ) ) {
// URL is for a registered intermediate size
$post_id = $row->post_id;
break;
}
}
else {
// see if URL is for a "backup" of an edited image
$backup_sizes = maybe_unserialize( $row->meta_value );
if ( in_array( $basename, wp_list_pluck( $backup_sizes, 'file' ) ) ) {
// URL is possibly for a "backup" of an edited image
// get the meta for the "original" attachment and perform the equivalent
// test we did above for '_wp_attachment_metadata' === $row->meta_key
$sql = $wpdb->prepare(
"SELECT meta_value
FROM $wpdb->postmeta
WHERE
post_id = %d AND meta_key = '_wp_attachment_metadata'",
$row->post_id
);
$meta = maybe_unserialize( $wpdb->get_var( $sql ) );
if ( isset( $meta['file'] ) && dirname( $meta['file'] ) === $dirname ) {
// URL is for a "backup" of an edited image
$post_id = $row->post_id;
break;
}
}
}
}
return $post_id;
}
endif; // function_exists( 'attachment_url_to_postid' )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment