diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 82ea8f27edc09..e16e5c7ff328c 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -5383,6 +5383,31 @@ function wp_maybe_generate_attachment_metadata( $attachment ) { function attachment_url_to_postid( $url ) { global $wpdb; + /** + * Filters the attachment ID to allow short-circuit the function. + * + * Allows plugins to short-circuit attachment ID lookups. Plugins making + * use of this function should return: + * + * - 0 (integer) to indicate the attachment is not found, + * - attachment ID (integer) to indicate the attachment ID found, + * - null to indicate WordPress should proceed with the lookup. + * + * Warning: The post ID may be null or zero, both of which cast to a + * boolean false. For information about casting to booleans see the + * {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. + * Use the === operator for testing the post ID when developing filters using + * this hook. + * + * @param int|null $post_id The result of the post ID lookup. Null to indicate + * no lookup has been attempted. Default null. + * @param string $url The URL being looked up. + */ + $post_id = apply_filters( 'pre_attachment_url_to_postid', null, $url ); + if ( null !== $post_id ) { + return (int) $post_id; + } + $dir = wp_get_upload_dir(); $path = $url; diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index d2edbc53747b1..b276289c353f1 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -1273,6 +1273,75 @@ public function test_attachment_url_to_postid_filtered() { remove_filter( 'upload_dir', array( $this, 'upload_dir' ) ); } + /** + * Test short-circuiting the attachment_url_to_postid filter. + * + * @ticket 61383 + */ + public function test_attachment_url_to_postid_short_circuit_filter_prevents_db_queries() { + $image_path = '2014/11/' . self::IMG_NAME; + $attachment_id = self::factory()->attachment->create_object( + $image_path, + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_type' => 'attachment', + ) + ); + $image_url = wp_get_attachment_url( $attachment_id ); + + add_filter( + 'pre_attachment_url_to_postid', + function () use ( $attachment_id ) { + return $attachment_id; + } + ); + + $queries_before = get_num_queries(); + $this->assertSame( $attachment_id, attachment_url_to_postid( $image_url ), 'The filter should short-circuit the function' ); + $queries_after = get_num_queries(); + $this->assertSame( 0, $queries_after - $queries_before, 'No database queries should be made by a short-circuited function' ); + } + + /** + * Test short-circuiting the attachment_url_to_postid filter with a not found result. + * + * @ticket 61383 + */ + public function test_attachment_url_to_postid_short_circuit_filter_when_attachment_does_not_exist() { + add_filter( 'pre_attachment_url_to_postid', '__return_zero' ); + + $queries_before = get_num_queries(); + $this->assertSame( 0, attachment_url_to_postid( 'http://example.org/wp-content/uploads/2014/11/image.jpg' ), 'The filter should short-circuit the function' ); + $queries_after = get_num_queries(); + $this->assertSame( 0, $queries_after - $queries_before, 'No database queries should be made by a short-circuited function' ); + } + + /** + * Test short-circuiting the attachment_url_to_postid filter with a proceed result. + * + * @ticket 61383 + */ + public function test_attachment_url_to_postid_short_circuit_filter_should_proceed_if_filter_returns_null() { + $image_path = '2014/11/' . self::IMG_NAME; + $attachment_id = self::factory()->attachment->create_object( + $image_path, + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_type' => 'attachment', + ) + ); + $image_url = wp_get_attachment_url( $attachment_id ); + + add_filter( 'pre_attachment_url_to_postid', '__return_null' ); + + $queries_before = get_num_queries(); + $this->assertSame( $attachment_id, attachment_url_to_postid( $image_url ), 'The filter should return the attachment ID' ); + $queries_after = get_num_queries(); + $this->assertGreaterThan( 0, $queries_after - $queries_before, 'Database queries are expected when the filter returns null' ); + } + public function upload_dir( $dir ) { $dir['baseurl'] = 'http://192.168.1.20.com/wp-content/uploads'; return $dir;