php 62 lines · 10 steps

Batch image resizing with PHP GD

A service that scales every image in a directory to fit within bounds while preserving type and transparency.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5use RuntimeException;
6 
7final class ImageResizer
8{
9 public function __construct(
10 private readonly int $maxWidth = 1920,
11 private readonly int $maxHeight = 1080,
12 private readonly int $jpegQuality = 82,
13 ) {}
14 
15 public function resizeDirectory(string $sourceDir, string $targetDir): array
16 {
17 if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
18 throw new RuntimeException("Cannot create target directory: {$targetDir}");
19 }
20 
21 $processed = [];
22 foreach (glob(rtrim($sourceDir, '/') . '/*.{jpg,jpeg,png}', GLOB_BRACE) as $path) {
23 $target = $targetDir . '/' . basename($path);
24 $this->resize($path, $target);
25 $processed[] = $target;
26 }
27 
28 return $processed;
29 }
30 
31 public function resize(string $source, string $target): void
32 {
33 [$width, $height, $type] = getimagesize($source) ?: throw new RuntimeException("Not an image: {$source}");
34 
35 $src = match ($type) {
36 IMAGETYPE_JPEG => imagecreatefromjpeg($source),
37 IMAGETYPE_PNG => imagecreatefrompng($source),
38 default => throw new RuntimeException("Unsupported type: {$source}"),
39 };
40 
41 $scale = min($this->maxWidth / $width, $this->maxHeight / $height, 1.0);
42 $newWidth = max(1, (int) round($width * $scale));
43 $newHeight = max(1, (int) round($height * $scale));
44 
45 $dst = imagecreatetruecolor($newWidth, $newHeight);
46 
47 if ($type === IMAGETYPE_PNG) {
48 imagealphablending($dst, false);
49 imagesavealpha($dst, true);
50 }
51 
52 imagecopyresampled($dst, $src, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
53 
54 match ($type) {
55 IMAGETYPE_JPEG => imagejpeg($dst, $target, $this->jpegQuality),
56 IMAGETYPE_PNG => imagepng($dst, $target, 6),
57 };
58 
59 imagedestroy($src);
60 imagedestroy($dst);
61 }
62}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Constructor property promotion with readonly gives you immutable, self-documenting configuration in one signature.
  2. 2Scaling by the minimum ratio capped at 1.0 fits an image inside a box without ever enlarging it.
  3. 3GD handles are raw resources, so freeing them with imagedestroy is essential to avoid leaking memory.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Batch image resizing with PHP GD — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code