Create responsive images on the fly with php and Imagick

To improve paging loading performance across multiple devices, web developers can provide each image in different resolutions using the srcset attribute and let the browser choose which one to load e.g.:

// on the page, where we want to display the image e.g.,
// index.php
<figure><img src="images/photo-1200.jpg"
    srcset="images/photo-1200.jpg 1200w,
        images/photo-1024.jpg 1024w,
        images/photo-800.jpg 800w,
        images/photo-600.jpg 600w" width="1200" height="800">
    <figcaption>the description of the photo</figcaption>
</figure>

Since we don’t want to have to create each image resolution manually, we use php library Imagick to resize the different image sizes on the fly and let the browser cache them. In order to do so, we need to pass the desired size to our php controller script /controller/img.php via the query string:

<?php
// index.php
// create different srcsets, preferably directly from a database record
$imgPath = 'images/photo-1200.jpg';
$origSize = getimagesize($imgPath);
$resizerPath = 'controller/img.php/'.$imgPath;
$photo = new stdClass();
$photo->src = $imgPath;
$photo->w = $origSize[0];
$photo->h = $origSize[1];
$photo->srcset = $photo->src.' '.$photo->w.'w, '.
    $resizerPath.'?w=1024 1024w, '.
    $resizerPath.'?w=800 800w, '.
    $resizerPath.'?w=600 600w';
$photo->title = 'the description of the photo';
?>
// render the html and the scrcset attribute
<figure><img src="<?php echo $photo->src; ?>"
    srcset="<?php echo $photo->srcset; ?>" width="<?php echo $photo->w; ?>" height="<?php echo $photo->h; ?>">
    <figcaption><?php echo $photo->title; ?></figcaption>
</figure>

The php script /controller/img.php, which creates the images with the different resolutions on the fly:

// img.php
$newWidth = $_GET['w'];
$img = new Imagick($imgPath);
$img->thumbnailImage($newWidth, $newWidth, true);
header('Content-Type: '.$img->getImageMimeType());
echo $img->getImageBlob();

To improve caching, we add an ETag header:

// img.php
if (isset($_SERVER['PATH_INFO'], $_GET['w'])) {
    $imgPath = __DIR__.'/../../../..'.$_SERVER['PATH_INFO'];

    // set the headers to hint for caching
    $etag = md5($imgPath.$_GET['w']);   // include the width to create different versions
    $etagHeader = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false;
    header('ETag: "'.$etag.'"');    // not the parentheses
    header('Cache-Control: max-age=86400');
    if ($etagHeader === $etag) {
        http_response_code(304);    // 304 Not Modified
    } else {
        // resize the image
        $newWidth = $_GET['w'];
        $img = new Imagick($imgPath);
        $img->thumbnailImage($newWidth, $newWidth, true);
        header('Content-Type: '.$img->getImageMimeType());
        echo $img->getImageBlob();
    }
} else {
    http_response_code(400);
}

Leave a comment

Your email address will not be published. Required fields are marked *