{"id":2260,"date":"2024-01-29T18:48:16","date_gmt":"2024-01-29T17:48:16","guid":{"rendered":"https:\/\/www.speich.net\/articles\/?p=2260"},"modified":"2024-01-29T19:00:27","modified_gmt":"2024-01-29T18:00:27","slug":"create-responsive-images-on-the-fly-with-php-and-imagick","status":"publish","type":"post","link":"https:\/\/www.speich.net\/articles\/en\/2024\/01\/29\/create-responsive-images-on-the-fly-with-php-and-imagick\/","title":{"rendered":"Create responsive images on the fly with php and Imagick"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">To <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/HTML\/Multimedia_and_embedding\/Responsive_images\" target=\"_blank\" rel=\"noreferrer noopener\">improve paging loading performance<\/a> across multiple devices, web developers can provide each image in different resolutions using the <code>srcset<\/code> attribute and let the browser choose which one to load e.g.:<\/p>\n\n\n\n<pre class=\"wp-block-code lang-html\"><code>\/\/ on the page, where we want to display the image e.g.,\n\/\/ index.php\n&lt;figure&gt;&lt;img src=\"images\/photo-1200.jpg\"\n    srcset=\"images\/photo-1200.jpg 1200w,\n        images\/photo-1024.jpg 1024w,\n        images\/photo-800.jpg 800w,\n        images\/photo-600.jpg 600w\" width=\"1200\" height=\"800\"&gt;\n    &lt;figcaption&gt;the description of the photo&lt;\/figcaption&gt;\n&lt;\/figure&gt;<\/code><\/pre>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\">Since we don&#8217;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 <code>\/controller\/img.php<\/code> via the query string:<\/p>\n\n\n\n<pre class=\"wp-block-code language-php\"><code>&lt;?php\n\/\/ index.php\n\/\/ create different srcsets, preferably directly from a database record\n$imgPath = 'images\/photo-1200.jpg';\n$origSize = getimagesize($imgPath);\n$resizerPath = 'controller\/img.php\/'.$imgPath;\n$photo = new stdClass();\n$photo-&gt;src = $imgPath;\n$photo-&gt;w = $origSize&#91;0];\n$photo-&gt;h = $origSize&#91;1];\n$photo-&gt;srcset = $photo-&gt;src.' '.$photo-&gt;w.'w, '.\n    $resizerPath.'?w=1024 1024w, '.\n    $resizerPath.'?w=800 800w, '.\n    $resizerPath.'?w=600 600w';\n$photo-&gt;title = 'the description of the photo';\n?&gt;\n\/\/ render the html and the scrcset attribute\n&lt;figure&gt;&lt;img src=\"&lt;?php echo $photo-&gt;src; ?&gt;\"\n    srcset=\"&lt;?php echo $photo-&gt;srcset; ?&gt;\" width=\"&lt;?php echo $photo-&gt;w; ?&gt;\" height=\"&lt;?php echo $photo-&gt;h; ?&gt;\"&gt;\n    &lt;figcaption&gt;&lt;?php echo $photo-&gt;title; ?&gt;&lt;\/figcaption&gt;\n&lt;\/figure&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The php script <code>\/controller\/img.php<\/code>, which creates the images with the different resolutions on the fly:<\/p>\n\n\n\n<pre class=\"wp-block-code language-php\"><code>\/\/ img.php\n$newWidth = $_GET&#91;'w'];\n$img = new Imagick($imgPath);\n$img-&gt;thumbnailImage($newWidth, $newWidth, true);\nheader('Content-Type: '.$img-&gt;getImageMimeType());\necho $img-&gt;getImageBlob();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To improve caching, we add an ETag header:<\/p>\n\n\n\n<pre class=\"wp-block-code language-php\"><code>\/\/ img.php\nif (isset($_SERVER&#91;'PATH_INFO'], $_GET&#91;'w'])) {\n    $imgPath = __DIR__.'\/..\/..\/..\/..'.$_SERVER&#91;'PATH_INFO'];\n\n    \/\/ set the headers to hint for caching\n    $etag = md5($imgPath.$_GET&#91;'w']);   \/\/ include the width to create different versions\n    $etagHeader = isset($_SERVER&#91;'HTTP_IF_NONE_MATCH']) ? trim($_SERVER&#91;'HTTP_IF_NONE_MATCH']) : false;\n    header('ETag: \"'.$etag.'\"');    \/\/ not the parentheses\n    header('Cache-Control: max-age=86400');\n    if ($etagHeader === $etag) {\n        http_response_code(304);    \/\/ 304 Not Modified\n    } else {\n        \/\/ resize the image\n        $newWidth = $_GET&#91;'w'];\n        $img = new Imagick($imgPath);\n        $img->thumbnailImage($newWidth, $newWidth, true);\n        header('Content-Type: '.$img->getImageMimeType());\n        echo $img->getImageBlob();\n    }\n} else {\n    http_response_code(400);\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>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.:<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[45],"class_list":["post-2260","post","type-post","status-publish","format-standard","hentry","category-php","tag-html","entry"],"_links":{"self":[{"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/posts\/2260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/comments?post=2260"}],"version-history":[{"count":21,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/posts\/2260\/revisions"}],"predecessor-version":[{"id":2437,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/posts\/2260\/revisions\/2437"}],"wp:attachment":[{"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/media?parent=2260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/categories?post=2260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.speich.net\/articles\/wp-json\/wp\/v2\/tags?post=2260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}