WP is showing wrong image dimensions

How to get real image dimension of already uploaded images which
dimensions are bigger than one now set in media settings
?

(Note: The italic and bold formatting above was added by me.)

wp_get_attachment_image_src() indeed doesn’t necessarily return the actual width and height of the resized image file, and that the function relies upon the specific image size’s max width and height set via the media settings screen (see wp-admin → Settings → Media).

Sample test case:

  1. I uploaded a 1600px × 1199px image (let’s call it foo-image.jpg), and the Medium image size’s max width was 300px (the default one). So WordPress saved that original image file and generated several thumbnails, and as for the Medium-sized thumbnail, the actual dimension is 300px × 225px and named foo-image-300x225.jpg.

    And wp_get_attachment_image_src() returned 300 (width) and 225 (height) which is the actual dimension.

  2. Then I changed the max width (setting) from 300px to 200px.

    And then wp_get_attachment_image_src() returned 200 (width) and 150 (height).

    And that is not actually wrong, but it’s just not the actual dimension of the actual thumbnail.

So why so, is because wp_get_attachment_image_src() uses image_downsize() which uses image_constrain_size_for_editor() which scales down the actual/default size of an image and which is the function that returns that “wrong” image dimension (1500px × 1000px in your case, and 200px × 150px in the above test case).

But remember, WordPress performs the scaling for a good reason — “This is so that the image is a better fit for the editor and theme.” — copied from the image_constrain_size_for_editor()‘s function reference.

So if you want to retrieve the actual dimension which are saved in a post metadata (see wp_get_attachment_metadata()), you can first, of course use wp_get_attachment_metadata() and access the relevant array items, but a simpler way is using image_get_intermediate_size() — moreover, it includes the image path relative to the uploads directory (wp-content/uploads), and the full image URL:

$thumbnail_id = get_post_thumbnail_id( $post->ID );

$medium_image_data = image_get_intermediate_size( $thumbnail_id, 'medium' );
var_dump( $medium_image_data['width'], $medium_image_data['height'], $medium_image_data['url'] );
/* Sample output:
int(300)
int(225)
string(68) "https://example.com/wp-content/uploads/2021/05/foo-image-300x225.jpg"
*/

// Or using wp_get_attachment_metadata(), assuming that sizes.medium exists:
$image_metadata = wp_get_attachment_metadata( $thumbnail_id );
$medium_image_data = $image_metadata['sizes']['medium'];
var_dump( $medium_image_data['width'], $medium_image_data['height'] );
// here, $medium_image_data['url'] is NULL (it's not set)!

Note though, that the image_get_intermediate_size()‘s function reference says: (formatted for brevity, and $size is the second parameter for the function)

The $size parameter can be an array with the width and height
respectively. If the size matches the ‘sizes’ metadata array for width
and height, then it will be used. If there is no direct match, then
the nearest image size larger than the specified size will be
used.