tags:

views:

253

answers:

5

I have a php script which generates an image, and is used (mainly) like this:

<img src="user_image.php?id=[some_guid]" />

The script uses a class I wrote to display an image matching that ID. There are a number of things that could go wrong though, and each of them throws an exception. So I have something like this:

<?php

try {

    if( ! isset($_GET['id']) ) throw new Exception;

    $images = new User_Images;
    $images->display($_GET['id']);

} catch( Exception $e ) {

    header('location: images/link_error.png');

}

If I view this from the browser, everything is fine -- if there was an error the address in the address bar changes to images/link_error.png and displays that instead.

But when this script is used in an <img> tag, and there is an error grabbing the image, it doesn't show up at all.

Do header redirects not work this way? What is another way that I can do this?

update

There is no problem, browser redirects work perfectly this way, the issue was that my browser was caching the empty image that was returned before the redirect was put in. A hard refresh (Ctrl + F5 for Firefox) fixed it and it started working like normal.

+1  A: 

Interesting - I would expect a call to a <img> tag to work with a redirect. Anyway, it might not. I'd be interested to hear why not. Anyone?

The easiest workaround that comes to mind is passing through the error image using fopen() and fpasshtru().

Pekka
I see no reason why it shouldn't work / redirect. I've done stuff like this before and it always worked.
Alix Axel
Odd, am I doing the redirect right? Do I have to do something else with the headers?
Carson Myers
The header looks fine, but maybe you should analyze the script's response on a deeper level. I don't know whether Firebug can help - a text-level fetcher/browser like wget or lynx might.
Pekka
It doesn't work because by the time the header gets sent to the browser, output has already been sent - all of the HTML up to the <img> tag.
Thilo
Huh? The HTML containing the img tag, and the image are two different resources, are they not? `user_image.php` is rendered independently from the HTML.
Pekka
You're right Pekka, I confused the "output already sent" issue, it doesn't apply here. Seems you just can't send a 301 as the source of an img tag. Not sure if that's defined in the HTML spec somewhere or just a browser implementation issue?
Thilo
It seems like the issue is solved and it worked with a redirect. Check out the discussion in Alix Axel's answer.
Pekka
A: 

A lot of places like PhotoBucket just display an error image when there are problems loading the requested one. This is usually a pre-constructed image that just says "Image Error" or whatever. When images are being put together dynamically, there aren't many other options than this other than returning a 404.

George Edison
He is doing that...
Alix Axel
Good point. I misread the question :(
George Edison
+1  A: 

Couldn't you just give the error image a default ID (i.e. zero) and use $images->display(0)? Then the error image is handled exactly like the successful case.

Thilo
Yes, I was hoping to avoid adding special cases to the class functions though, so far everything just throws an exception and they are handled at the level that they need to be
Carson Myers
Well, the problem is that you're doing a redirect (header: Location...) inside an <img> tag... that doesn't work. It does work calling the script in the browser since you're redirecting the whole page there. You need to return an image from this script even in the error case. If you don't want it to be part of the function, fopen it in the exception and send the file contents after the header "content-type: image/png".
Thilo
I suppose I will have to, although I am extra curious about this now since Alix Axel said he has done this before and it worked
Carson Myers
@Carson: Actually I think Thilo may be right, I haven't done it *exactly* the same way you are. Still if Thilo is indeed right the `headers_sent()` function in my answer should catch that scenario.
Alix Axel
See my comment below. You can't send a 301 redirect after you've already sent HTML. That's why it works calling this directly in the browser, but not from inside an <img> tag.
Thilo
There wasn't any HTML sent before the redirect -- fetching the images on the page is done in separate requests. Also it turns out that my browser cached _nothing_ as the image that wasn't showing up, so it wasn't even trying to get the broken link picture.
Carson Myers
Thilo, I think you're wrong. A call to an image resource is essentially identical with a call to any other resource. All resources should be able to return a 301 header, and cause the browser to look at the new address. I need to see it black and white that it won't work for images before I believe this. Also, the rendering of the HTML and the rendering of the image are two entirely separate processes, and the fact that HTML has been output in one process, does not affect the image file and its ability to emit `header()`s in the slightest. Unless I am totally overlooking something.
Pekka
Hmm... I remember having this same problem and that was my solution, but I'm open to corrections :) In any case, sending the error image as image/png directly should do the trick, no?
Thilo
Ya. That's your best bet.
George Edison
+1  A: 

Hummm... My guess is that the

header('Location: images/link_error.png');

Because headers were previously sent?

Try placing an ob_start() on the top of your file and see if it solves your problem.


Here is a simple way to debug this:

try {
    if (!isset($_GET['id'])) throw new Exception;

    $images = new User_Images;
    $images->display($_GET['id']);
} catch (Exception $e) {

    if (headers_sent() === false)
    {
        header('Location: images/link_error.png'); // also try using the absolute URL here
    }

    else
    {
        echo file_get_contents('http://www.google.com/intl/en_ALL/images/logo.gif');
    }
}

If the Google logo shows up, you need to trace where you're outputting data or use ob_start() + ob_end_clean().


this tag is really nice!

Alix Axel
Mmmm, good point! But according to his source code, no output takes place. (If it's the full code.)
Pekka
it isn't that, because visiting the page gives the correct result, and when the browser goes to fetch an image it does it in a separate request so the headers from the page including the image tag have nothing to do with it.
Carson Myers
good idea, I tried it but unfortunately the image still simply doesn't show.
Carson Myers
@Carson: Have you tried it using the `images/link_error.png` absolute URL?
Alix Axel
Try dropping the whole code and just do the header redirect, does that work?
Alix Axel
okay, I put `header('....etc` at the top of the source file, and it works fine -- what's interesting is that the existing images _still show up_ even though the redirect is at the top of the source file... maybe caching has been making this more elusive than necessary?
Carson Myers
ah yes, I put the `header()` call back where it belonged and did a hard refresh and it worked -- I didn't think browsers would cache _nothing_ for image files...
Carson Myers
@Carson: So, your problem is solved then? :)
Alix Axel
Indeed, thanks for the help! And uh... Sorry for not trying `ctrl+F5` a little earlier :)
Carson Myers
Browsers have a funny way of caching php pages/images despite http headers specifying otherwise.
George Edison
@Carson: It happens, no problem! Glad you solved it.
Alix Axel
A: 

There is no problem, browser redirects work perfectly this way, the issue was that my browser was caching the empty image that was returned before the redirect was put in. A hard refresh (Ctrl + F5 for Firefox) fixed it and it started working like normal

uggh.. this was exactly my problem too. and i had a whole convoluted solution with eval(include(...)) that didn't work either!

Justin