1

I have this short external php script (named resize.php) which I am calling to from other php scripts when I want to resize/thumbnail images. I would like to change the script to work as an internal function instead of an external php script and I don't know how to convert it to a function that will do the job.

This is how it is being called:

<img src="resize.php?image=images/IMG228.jpg&amp;width=165" border="0" alt="foo" width="165" height="165" title="">

The main reason I want to change it to be a function is because due to caching issues, when it is an external php script, if it has already been called/loaded by the browser, it doesn't load it again, even if the image on the server (to be resized) is newer, and despite the fact that the resizer.php itself had a caching detection and refresh implemented. If it is an internal resizer function, I assume this issue will be resolved.

This is the resizer.php script:

<?php
if (isset($_GET['image']) && isset($_GET['width']) && is_numeric($_GET['width'])) {
    // Get image name 
    $original_image = $_GET['image'];
    // Watermarks
    $wmark          = 'watermark.png'; //largest watermark
    $wmarkm         = 'watermark_m.png'; //medium watermark
    $wmarks         = 'watermark_s.png'; //smallest watermark
    $wmarkno        = 'nowatermark.png'; //No watermark

    // Maximum image width 
    $max_width  = (int) $_GET['width'];
    // Maximum image height 
    $max_height = "800";

    if (file_exists($original_image)) {
        $cached = 'cache/' . preg_replace('/(\.\w+$)/', ".{$max_width}\\1", $original_image);

        if (file_exists($cached)) {
            $cst = stat($cached);
            $fst = stat($original_image);
            if ($fst[9] <= $cst[9] && $fst[10] <= $cst[10]) {
                if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $cst[9]) {
                    // header("HTTP/1.0 304 Not Modified");
                    header("HTTP/1.1 304 Not Modified");
                    // header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($cached)).' GMT', true, 304);
                } else {

                    header('Content-type: image/jpeg');
                    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cst[9]) . ' GMT');
                    header('Cache-Control: private');
                    // print file_get_contents($cached);
                    readfile("{$cached}");
                }
                exit;
            }
        }

        if ($max_width > 300) {
            $watermark = $wmark;
        } elseif ($max_width > 152 && $max_width < 300) {
            $watermark = $wmarkm;
        } elseif ($max_width > 50 && $max_width < 151) {
            $watermark = $wmarks;
        } else {
            $watermark = $wmarkno;
        }

        //create the resized image
        exec("gm convert -filter Lanczos {$original_image} -thumbnail {$max_width}x{$max_height} -quality 90 -unsharp 2x0.5+0.7+0 {$cached}");
        //apply the watermark and recreate the watermarked image, overwriting the previously resized image
        exec("gm composite -quality 90 -dissolve 100 -gravity center {$watermark} {$cached} {$cached}");

        header('Content-type: image/jpeg');
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
        header('Cache-Control: private');
        readfile("{$cached}");
    }
}
?>
Nikita 웃
  • 2,015
  • 14
  • 40
  • This is an XY problem it sounds like. Append a random number as a get variable to the end of the `src` value, such as `src="resize.php?image=images/IMG228.jpg&width=165&r== rand() ?>"` – Qix - MONICA WAS MISTREATED May 09 '14 at 17:02
  • Or you can [send down some headers](http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers). – Qix - MONICA WAS MISTREATED May 09 '14 at 17:05
  • thanks, I already did that, but it completely prevents caching and reloads the script as a different script on every image call, which slows page load. I only want it to replace images when they are newer than cached.... – Nikita 웃 May 09 '14 at 17:05
  • That's the whole point of caching. If you want the browser to handle caching, you're going to face a delay from which you update the image on the server and the browser decides to invalidate that element in the cache. – Qix - MONICA WAS MISTREATED May 09 '14 at 17:07
  • I'd recommend caching server side, forcing non-caching on the browser, and then just invalidate your cache when you generate a new image. – Qix - MONICA WAS MISTREATED May 09 '14 at 17:07
  • But how do I add more headers to this code? It already supposed to be sending headers for the image. Is there a way to send headers regarding the resize.php script itself? – Nikita 웃 May 09 '14 at 17:10
  • Sure, using the [`header()`](http://www.php.net/manual/en/function.header.php) function. – Qix - MONICA WAS MISTREATED May 09 '14 at 17:14
  • @Qix Can you kindly post an example of my full code with the header() function you refer to? – Nikita 웃 May 09 '14 at 17:25
  • Sure, I have done a lot of research prior to posting and have tried adding headers to the script, but the thing is that it seems to collide with the headers I am sending for the image itself, which is why I asked you to show how. As for the "whole point of caching", I am caching both server-side and in the browser and I only want to tell the browser to load new images when needed and just use its own cache (304) when the image is older, not let the browser "decide" when to invalidate. I am sure there are ways to do that. – Nikita 웃 May 09 '14 at 17:36

1 Answers1

1

Like this?

img_resize.php

<?php
function resizeImage($image, $width)
{
    if(file_exists('c:/server/path/to/images/folder.'.$image) && ctype_digit($width) && $width >= 1)
    {
        // awesome image resizing code
    }
}
?>

some frontend script which the user uploads their picture to: resize.php ?

<?php
include_once('c:/path/to/img_resize.php');

resizeImage('user12345_profilepic.jpg', 165);
?>
MonkeyZeus
  • 18,445
  • 3
  • 30
  • 67
  • Thanks. Tried putting my code inside this function, but since it supposed to be sending headers, I just get the "Cannot modify header information - headers already sent by..." error – Nikita 웃 May 09 '14 at 17:07
  • The `Cannot modify....` issue is covered countless times on this website alone so I won't be going over that. May I ask why you are converting images on the fly anyways and also why are you using PHP to read and serve images? It takes a heck of a lot more overhead to have PHP read and output files as opposed to letting the web server find and serve them. I strongly recommend just sizing the images only once and storing them as `image_x165.jpg` or whatever. There is no reason to invoke PHP every time an image is needed. – MonkeyZeus May 09 '14 at 17:14
  • If your site needs to pull in say 25 of these images at once then your server is going to get overloaded in a matter of seconds especially if multiple people request that page at the same exact time because the file doesn't exist when they get to your page so 10 visitors could spawn 250 unnecessary resizing processes. – MonkeyZeus May 09 '14 at 17:16
  • yes, I know that "Cannot modify headers" issue well, that's because the headers were sent too late by the function, which is one of the reason I posted my request to have this script modified to a function without having such issues. As for the resizing, that's not a problem, since the script already has a server caching detection and mechanism and if the image was already resized - it just loads it straight for the server-side cache, as you can see, else it just resize it on the fly and loads it from the cache, but I am just u/l 100 images per day, so that's a non-issue. – Nikita 웃 May 09 '14 at 17:23
  • For headers already sent, if you can't help it, perhaps [output buffers](http://www.php.net/manual/en/book.outcontrol.php) would find a good home here. – Qix - MONICA WAS MISTREATED May 09 '14 at 17:30
  • @MonkeyZeus I forgot to add that I am doing the resizing on the fly because the same images are being called in multiple different sizes and each of them needs to have a watermark added to them after which they are being saved to the server (cached), and from that point on - as long as the original image is older, the resizer script is loading only the cached image, without any resizing done, so there is no affect on performance. – Nikita 웃 May 09 '14 at 17:46
  • I understand the whole saving to a cache folder idea but given the situation that a new visitor is requesting an image width of `165` which resides in your cache folder: Apache will have to spawn a child process which invokes PHP to serve the file (no resizing) rather than allowing the Apache child process to naturally serve the file without PHP intervention. If this resize function is being moved to the inside of a framework then the entire PHP framework is being loaded for every image request which a new visitor does not have cached in their browser. – MonkeyZeus May 09 '14 at 18:09
  • Actually PHP is being invoked anyway, together with the entire framework, in my specific case. I am using osCommerce ecommmerce framework to serve products details with their images, so I am not sure if doing it in a different way, the impact on my resources will be much different than the way we do it now, but I am open for suggestions. As for the function itself, I was trying to implement it, by removing all the header() lines, now it does show up the resized/cached images, but not changing them in the browser when I upload newer ones, as I don't send headers to inform of the change. Ideas? – Nikita 웃 May 09 '14 at 19:49