9

I've got an older piece of code that attempts to load an image file. Since it doesn't know what type it is, the code tries them all:

$res = @imagecreatefromjpeg($sourceName);
if ( $res === false)
    $res = @imagecreatefromgif($sourceName);
if ( $res === false)
    $res = @imagecreatefrompng($sourceName);
if ( $res === false) {
    $err[] = 'Cannot load file as JPEG, GIF or PNG!';

This used to work perfectly for a long time, but after a recent server migration it stopped working when using a PNG file (JPEG files work fine). Investigation revealed that the very first line produces a fatal error:

Fatal error: imagecreatefromjpeg(): gd-jpeg: JPEG library reports unrecoverable error: Not a JPEG file: starts with 0x89 0x50

Since the line has the error-suppression operator, the script simply stopped executing and returned HTTP 200 with an empty response.

What I don't understand is... wha... how... why... since when... huh?

Even the official documentation says that these functions return FALSE when unable to load a file, and the construct with the @ operator is shown there too. I can find nothing on the web which would say that "not a JPEG file" is a fatal error. Everywhere it's mentioned, it's a warning as it always has been. Why am I getting a fatal error now?

My PHP is 5.6.30, GD is there with everything.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • Have you had a look at [this](http://stackoverflow.com/a/10668155/2518525) and [this](http://stackoverflow.com/a/3901885/2518525) might be worth looking at (*handling buggy jpeg images*). – Darren Mar 20 '17 at 00:57
  • 1
    @Darren - both of those links speak about a **warning** that appears on failure. A warning that can be suppressed and ignored. I get a **fatal error** instead. Also, I already stated that it's not a buggy jpeg - it's a png and I expect this function to fail - just not fatally. – Vilx- Mar 20 '17 at 01:08

2 Answers2

6

I guess I can answer my own question. It's a known bug. See. PHP bugs #73514 and #73479. And actually the bug is in libgd, and has been reported there too. A fix was made last November, but still hasn't made it to a release. So, yeah... no luck. I'll try to use getimagesize() instead to determine the type first and use the appropriate function instead. That still won't help in the case of a genuinely corrupt file, but it's something.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • Pls check the image mime type as well. If it is different than it is also not works. I have faced the similar issue and i found that my image extension is .jpg and i am getting image/png in mime type of image, which is wrong. So, i have resolved it by making proper image mime type. – Bhatt Akshay Jul 22 '20 at 10:11
  • That file was uploaded from the user so the mime type headers are not reliable - and I don't even know if they can be accessed from PHP to begin with. – Vilx- Jul 22 '20 at 10:29
  • You can also setup the mime type of image while upload it on server. – Bhatt Akshay Jul 22 '20 at 11:33
  • @BhattAkshay - ... what? I don't understand what you mean. – Vilx- Jul 22 '20 at 21:01
  • first you need to understand what is MIME in image. Pls refer this document to understand about MIME in image : [File MIME type](https://en.wikipedia.org/wiki/MIME) – Bhatt Akshay Jul 25 '20 at 19:55
  • @BhattAkshay - I know perfectly well what MIME is. I also know that a JPEG/PNG/GIF files don't contain their own MIME types inside themselves. Given just a bunch of bytes you can't tell what kind of file it is. You need to either know it beforehand, or you can try guessing it. Guessing is commonly done it two ways - either by looking at the file extension (but that could be wrong) or by looking at the first few bytes. All three of the formats have certain fixed bytes by which they can be recognized. The latter is essentially what I'm doing with the `getimagesize()` function. – Vilx- Jul 25 '20 at 20:31
  • @BhattAkshay - so, tell me again, where exactly should I look for the image's MIME type? – Vilx- Jul 25 '20 at 20:32
  • So, now you need to check only that thing that is your file_name.jpg ==> .jpg extension is perfectely match with your file MIME type ==> image/jpg or not. If it is proper than your imagecreatefromjpeg function will 100% works. If it is not proper than you have to correct it first. – Bhatt Akshay Jul 25 '20 at 21:06
  • @BhattAkshay - OK, but from where do I get the MIME type of my image? – Vilx- Jul 25 '20 at 21:18
  • ```getimagesize('Path to your file or file url ');``` It will give you the file MIME type. – Bhatt Akshay Jul 25 '20 at 21:29
  • @BhattAkshay - That's EXACTLY what I've been saying - use `getimagesize()` to determine the type and then call the appropriate `createimagefromXXX()` function. – Vilx- Jul 25 '20 at 22:03
0

The problem is that yout file has bad extension, based on first bytes it is really PNG, not JPG. My solution is to detect first bytes and rename file to PNG:

$firstBytes = bin2hex(file_get_contents($tmp_name,false,null,0,2));
if ($firstBytes==8950) $_FILES['file']['name'].= '.png';
Tomas Šivickas
  • 977
  • 9
  • 10
  • The file extension is irrelevant to the `imagecreatefromXXX()` functions. Yes, I wanted to do exactly that - detect what the file is based on its contents, and then open it with the appropriate function. Looking at the first bytes is correct, but not always very simple. JPEG, for example, has 4 different possible headers. That's why I didn't want to do it myself but use some other library that knows all the quirks. For example, the `getimagesize()` function does that. – Vilx- May 12 '21 at 12:27