Skip to content

Conversation

@jaxwilko
Copy link
Member

@jaxwilko jaxwilko commented Mar 18, 2022

A simple PR that adds some php8.0 features and enforces return types.

Something of note, all the widths & heights should probably be ints but in reality they work off floats and changing them to int breaks the tests.

This PR lays groundwork for future improvements.

Summary by CodeRabbit

  • Refactor
    • Enhanced internal code structure and type safety for the image processing module, resulting in improved reliability and error prevention during image operations.

✏️ Tip: You can customize this high-level summary in your review settings.

@jaxwilko jaxwilko marked this pull request as draft March 18, 2022 16:14
@jaxwilko
Copy link
Member Author

There maybe a bug when trying to resize in crop mode where the calculated values (float) for width & height are passed to crop() and implicitly cast to int resulting in a potential issue.

Need to add more tests for better coverage of the $resizer->setOptions(['mode' => ...]) feature.

@LukeTowers LukeTowers added this to the 1.2.1 milestone Jul 15, 2022
Base automatically changed from wip/1.2 to develop July 15, 2022 06:59
@bennothommo bennothommo modified the milestones: 1.2.1, v1.2.2 Sep 12, 2022
@LukeTowers LukeTowers modified the milestones: v1.2.2, v1.2.3 Apr 20, 2023
@LukeTowers LukeTowers modified the milestones: v1.2.3, v1.2.4 Jul 7, 2023
@LukeTowers LukeTowers removed this from the v1.2.4 milestone Dec 27, 2023
@LukeTowers
Copy link
Member

@jaxwilko can you remerge develop into this branch and then let me know what's left on it?

@LukeTowers LukeTowers added this to the 1.3.0 milestone Apr 25, 2024
@bennothommo bennothommo added maintenance PRs that fix bugs, are translation changes or make only minor changes and removed Type: Maintenance labels Mar 28, 2025
@LukeTowers LukeTowers changed the base branch from develop to wip/1.3 July 24, 2025 05:29
@wintercms wintercms deleted a comment from github-actions bot Jul 24, 2025
@wintercms wintercms deleted a comment from github-actions bot Jul 24, 2025
@LukeTowers LukeTowers marked this pull request as ready for review July 24, 2025 05:31
@LukeTowers LukeTowers added the needs review Issues/PRs that require a review from a maintainer label Jul 24, 2025
@LukeTowers LukeTowers self-assigned this Jul 24, 2025
@github-actions github-actions bot closed this Nov 27, 2025
@LukeTowers LukeTowers reopened this Nov 27, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

A single file, Resizer.php, is refactored to introduce explicit PHP 7+/8+ typed properties and parameters across all public and protected members, replacing untyped or mixed declarations with concrete types such as FileObj, string, int, float, GdImage, and array.

Changes

Cohort / File(s) Summary
Type Hinting Refactoring
src/Database/Attach/Resizer.php
Added explicit type hints to all class properties (e.g., FileObj, string, int, float, GdImage, array, nullable types) and strengthened method signatures with parameter types and return types. Updated constructor and factory method to accept FileObj|string. Converted dimension-related methods to use float parameters/returns. No functional behavior changes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding PHP 8.0 features (typed properties, return types, union types, match expressions) to the Resizer class.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@wintercms wintercms deleted a comment from github-actions bot Jan 27, 2026
@wintercms wintercms deleted a comment from github-actions bot Jan 27, 2026
@wintercms wintercms deleted a comment from github-actions bot Jan 27, 2026
@LukeTowers
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/Database/Attach/Resizer.php (5)

359-364: Use round() for crop offsets to prevent pixel shift.
The calculation produces floats ($optimalWidth / 2 and $newWidth / 2), but crop() expects int parameters. PHP implicitly truncates floats to integers, which can misalign the crop by up to 1 pixel. Rounding provides more accurate centering for this calculation.

Suggested fix
-            $cropStartX = ($optimalWidth  / 2) - ($newWidth  / 2) - $offset[0];
-            $cropStartY = ($optimalHeight / 2) - ($newHeight / 2) - $offset[1];
+            $cropStartX = (int) round(($optimalWidth  / 2) - ($newWidth  / 2) - $offset[0]);
+            $cropStartY = (int) round(($optimalHeight / 2) - ($newHeight / 2) - $offset[1]);

523-555: Add validation for failed image creation before returning.

imagecreatefrom*() functions return false on failure (e.g., corrupted or invalid image files), but the function signature declares GdImage as the return type without a union. Returning false violates the type contract and will cause type-checking failures. Additionally, retainImageTransparency() is called with potentially false values, causing secondary type violations.

Suggested fix
-        return $img;
+        if ($img === false) {
+            throw new Exception('Failed to open image from file.');
+        }
+        return $img;

320-347: Add error checks after GD function calls before assigning to typed GdImage properties.

getRotatedOriginal(), imagescale(), and imagecreatetruecolor() can return false on failure (memory exhaustion, invalid dimensions, etc.). With typed GdImage properties, assigning false will throw a TypeError. Add explicit checks:

Suggested fix
         // Get the original image rotated according to exif orientation
         $rotatedOriginal = $this->getRotatedOriginal();
+        if ($rotatedOriginal === false) {
+            throw new Exception('Failed to rotate source image.');
+        }

         if ($this->mime === 'image/gif') {
             // Use imagescale() for GIFs, as it produces better results
             $imageResized = imagescale($rotatedOriginal, $optimalWidth, $optimalHeight, IMG_NEAREST_NEIGHBOUR);
+            if ($imageResized === false) {
+                throw new Exception('Failed to scale image.');
+            }
             $this->retainImageTransparency($imageResized);
         } else {
             // Resample - create image canvas of x, y size
             $imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
+            if ($imageResized === false) {
+                throw new Exception('Failed to create resize canvas.');
+            }
             $this->retainImageTransparency($imageResized);

81-107: Guard against null MIME/extension and initialize $this->file.

Symfony\Component\HttpFoundation\File\File::guessExtension() and getMimeType() both return ?string (nullable), which will throw a TypeError when assigned to the non-nullable $extension and $mime properties. Additionally, the $file property declared on line 34 is never initialized in the constructor.

Suggested fix
+        $this->file = $file;
         // Get the file extension
-        $this->extension = $file->guessExtension();
-        $this->mime = $file->getMimeType();
+        $extension = $file->guessExtension() ?: pathinfo($file->getPathname(), PATHINFO_EXTENSION);
+        $mime = $file->getMimeType();
+        if (!$extension || !$mime) {
+            throw new Exception('Unable to determine image extension or MIME type.');
+        }
+        $this->extension = $extension;
+        $this->mime = $mime;

410-433: Use nullable ints for optional params and guard crop canvas creation.

The parameters int $srcWidth = null and int $srcHeight = null use implicit nullable syntax, which is invalid in PHP 8.2+. They must be explicitly declared as ?int $srcWidth = null and ?int $srcHeight = null.

Additionally, imagecreatetruecolor() returns GdImage|false, but the result is passed directly to retainImageTransparency() which expects GdImage without checking for failure. Add a guard to handle the false case:

✅ Suggested fix
-        int $srcWidth = null,
-        int $srcHeight = null
+        ?int $srcWidth = null,
+        ?int $srcHeight = null
     ): static {
...
-        $imageResized = imagecreatetruecolor($newWidth, $newHeight);
+        $imageResized = imagecreatetruecolor($newWidth, $newHeight);
+        if ($imageResized === false) {
+            throw new Exception('Failed to create crop canvas.');
+        }

@LukeTowers
Copy link
Member

@jaxwilko the robot has some feedback

@jaxwilko
Copy link
Member Author

@LukeTowers robot knows not of the fact that variables type hinted as int and docblocked as int are actually floats that get chopped when passing because php is weird

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance PRs that fix bugs, are translation changes or make only minor changes needs review Issues/PRs that require a review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants