create a backup of the original image#98
create a backup of the original image#98tijmenbruggeman wants to merge 18 commits intotinify:masterfrom
Conversation
| } | ||
|
|
||
| /** | ||
| * creates a backup of the image as <originalfile>.bak.<extension> |
There was a problem hiding this comment.
To not interfere with existing plugins and whatever watches the file upload folder. Maybe write the files to another folder?
e.g
TinifyBackups/{original_path}
There was a problem hiding this comment.
Pull request overview
Adds an opt-in setting to create a .bak backup of the original (uncompressed) uploaded image before the plugin compresses it, exposing the option in the settings UI and adding unit tests around the new behavior.
Changes:
- Introduces a new
tinypng_backupsetting (registered in settings and surfaced in the “Original image” settings view). - Adds backup creation logic to
Tiny_Image::compress()(backup named<filename>.bak.<ext>). - Adds unit tests and test helpers/docs to validate backup behavior.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/class-tiny-image.php |
Creates a backup prior to compression and logs whether it was created. |
src/class-tiny-settings.php |
Registers the new setting and adds get_backup_enabled() for feature gating. |
src/views/settings-original-image.php |
Adds a checkbox to enable/disable backups in the settings UI. |
test/unit/TinyImageTest.php |
Adds tests asserting when backups should/shouldn’t be created. |
test/unit/TinySettingsAdminTest.php |
Updates expectations for settings registration to include the new key. |
test/helpers/wordpress.php |
Updates helper docs used by unit tests (image creation/metadata). |
test/unit/TinyTestCase.php |
Adds docblock for WP stubs property. |
src/class-tiny-compress.php |
Improves compress_file parameter documentation. |
bin/run-mocks |
Updates docker compose invocation to include --wait. |
Comments suppressed due to low confidence (1)
src/class-tiny-compress.php:103
- PHPDoc for compress_file is inaccurate: the method returns the $details array, not void. Also, "@param array{ string }" is not a valid shape for a list of mimetypes; consider documenting this as "array" (or "string[]") for $convert_to.
* @param string $file path to file
* @param array $resize_opts
* @param array $preserve_opts
* @param array{ string } conversion options
* @return void
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $this->update_tiny_post_meta(); | ||
| $resize = $this->settings->get_resize_options( $size_name ); | ||
| $preserve = $this->settings->get_preserve_options( $size_name ); | ||
| $backup_created = $this->create_backup( $size_name, $size->filename ); |
There was a problem hiding this comment.
Backup creation is triggered for every unprocessed size (including sizes that are only being processed for conversion). This can create/overwrite the .bak file from an already-compressed original, which defeats the goal of backing up the uncompressed upload. Consider only creating the backup when the original is actually uncompressed (e.g., gate on the Tiny_Image_Size::uncompressed() state) and avoid backing up during conversion-only runs.
| $backup_created = $this->create_backup( $size_name, $size->filename ); | |
| $backup_created = false; | |
| if ( method_exists( $size, 'uncompressed' ) && $size->uncompressed() ) { | |
| $backup_created = $this->create_backup( $size_name, $size->filename ); | |
| } |
| $fileinfo['filename'], | ||
| $fileinfo['extension'] | ||
| ); | ||
|
|
There was a problem hiding this comment.
copy() will overwrite an existing backup file. If compress() is re-run later (e.g., conversion enabled after initial compression), this can silently replace the backup with a newer (possibly already-compressed) file. Consider skipping backup creation when the backup already exists (or use a unique/immutable naming strategy) so the first backup remains the original upload.
| if ( file_exists( $backup_file ) ) { | |
| // Backup already exists; keep the first backup (original upload) intact. | |
| return true; | |
| } |
| * Retrieves the preserve options for the original image | ||
| * | ||
| * @param string - size name | ||
| * @return false|array<string> false if size is not original, otherwise array of preserved keys |
There was a problem hiding this comment.
Docblock for get_preserve_options has an invalid @param tag (missing variable name) and the return type description is a bit inconsistent. Use a standard form like "@param string $size_name" and consider "array|false" for the return type to match PHPDoc conventions.
| * Retrieves the preserve options for the original image | |
| * | |
| * @param string - size name | |
| * @return false|array<string> false if size is not original, otherwise array of preserved keys | |
| * Retrieves the preserve options for the original image. | |
| * | |
| * @param string $size_name Size name. | |
| * @return array<string>|false Array of preserved keys if size is original, or false otherwise. |
| $settings = new Tiny_Settings(); | ||
| $mock_compressor = $this->createMock(Tiny_Compress::class); | ||
| $settings->set_compressor($mock_compressor); | ||
|
|
||
| $metadata = $this->wp->getTestMetadata($input_dir, $input_name); |
There was a problem hiding this comment.
This test sets a Tiny_Compress mock but doesn’t stub compress_file(). The mock will return null, which means the code under test doesn’t exercise/validate handling of a real compressor response and could become fragile if Tiny_Image starts assuming an array response. Consider stubbing compress_file() to return a minimal valid details array and optionally asserting it was called once for the original size.
| $settings = new Tiny_Settings(); | ||
| $mock_compressor = $this->createMock(Tiny_Compress::class); | ||
| $settings->set_compressor($mock_compressor); | ||
|
|
||
| $metadata = $this->wp->getTestMetadata($input_dir, $input_name); |
There was a problem hiding this comment.
Same as above: compress_file() isn’t stubbed on the Tiny_Compress mock, so the test doesn’t validate behavior with a real compressor response and may become brittle. Consider stubbing compress_file() to return a minimal valid details array (and assert call count) to keep the test meaningful.
| $settings = new Tiny_Settings(); | ||
| $mock_compressor = $this->createMock(Tiny_Compress::class); | ||
| $settings->set_compressor($mock_compressor); | ||
|
|
||
| $metadata = $this->wp->getTestMetadata($input_dir, $input_name); | ||
| $tinyimg = new Tiny_Image($settings, 999, $metadata); |
There was a problem hiding this comment.
Same as above: the Tiny_Compress mock isn’t configured to return a realistic value from compress_file(). Stubbing a minimal response would make this test less fragile and better aligned with actual runtime behavior.
|
|
||
| /** | ||
| * Creates images on the virtual disk for testing | ||
| * @param null|array $sizes Array of size => bytes to create, file will be named $name-$size.png |
There was a problem hiding this comment.
The createImages() docblock says generated files will be named "$name-$size.png", but the implementation uses the array key (size name) in the filename ("$name-$key.png"). Update the docblock wording so it matches what the helper actually writes.
| * @param null|array $sizes Array of size => bytes to create, file will be named $name-$size.png | |
| * @param null|array $sizes Array of size name (array key) => bytes to create; each file will be named "$name-<size name>.png" |
| * | ||
| * @param string $path directory of the file in UPLOAD_DIR | ||
| * @param string $name name of the file without extension | ||
| * @return array object containing metadata |
There was a problem hiding this comment.
Docblock says "@return array object containing metadata", but getTestMetadata() returns a plain array. Consider updating the @return description to avoid confusion for test authors.
| * @return array object containing metadata | |
| * @return array metadata array |
This will add the option to create a backup of the original uncompressed image.
Changes
Further development
further development before we release it