From 3d81f3e55fbb56521e8406c99e55c27cfb56712a Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 8 May 2026 20:23:34 +0200 Subject: [PATCH 01/34] rename meta key to private meta key --- src/config/class-tiny-config.php | 2 +- test/unit/TinyImageTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/class-tiny-config.php b/src/config/class-tiny-config.php index dfab8f81..6378578c 100644 --- a/src/config/class-tiny-config.php +++ b/src/config/class-tiny-config.php @@ -9,5 +9,5 @@ class Tiny_Config { const SHRINK_URL = 'https://api.tinify.com/shrink'; const KEYS_URL = 'https://api.tinify.com/keys'; const MONTHLY_FREE_COMPRESSIONS = 500; - const META_KEY = 'tiny_compress_images'; + const META_KEY = '_tiny_compress_images'; } diff --git a/test/unit/TinyImageTest.php b/test/unit/TinyImageTest.php index f70f3b4a..114e17c8 100644 --- a/test/unit/TinyImageTest.php +++ b/test/unit/TinyImageTest.php @@ -17,7 +17,7 @@ public function set_up() { } public function test_tiny_post_meta_key_may_never_change() { - $this->assertEquals( '61b16225f107e6f0a836bf19d47aa0fd912f8925', sha1( Tiny_Config::META_KEY ) ); + $this->assertEquals( '438fc52ce17b9aedf0cf70dea52d5551affba59a', sha1( Tiny_Config::META_KEY ) ); } public function test_update_wp_metadata_should_not_update_with_no_resized_original() { From 5d99e3a2cff029ff3a39f974671aa16c757ca87a Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 8 May 2026 20:23:57 +0200 Subject: [PATCH 02/34] add migration 370 --- src/class-tiny-migrate.php | 95 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/class-tiny-migrate.php diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php new file mode 100644 index 00000000..3c3940f5 --- /dev/null +++ b/src/class-tiny-migrate.php @@ -0,0 +1,95 @@ +=' ) ) { + return; + } + + if ( version_compare( $stored_version, '3.7.0', '<' ) ) { + self::migrate_370(); + } + + update_option( self::DB_VERSION_OPTION, self::DB_VERSION ); + } + + /** + * Migrates the tiny meta key from public to private. + * + * Renames all `tiny_compress_images` post meta entries to + * `_tiny_compress_images`. + * + * @since 3.7.0 + * + * @return void + */ + private static function migrate_370() { + global $wpdb; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->update( + $wpdb->postmeta, + array( 'meta_key' => '_tiny_compress_images' ), + array( 'meta_key' => 'tiny_compress_images' ), + array( '%s' ), + array( '%s' ) + ); + } +} From 159d5e7730d276658749ab098525aa5688957880 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 8 May 2026 20:24:29 +0200 Subject: [PATCH 03/34] run migration on plugin load --- tiny-compress-images.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tiny-compress-images.php b/tiny-compress-images.php index e6aa9cad..a1412f47 100644 --- a/tiny-compress-images.php +++ b/tiny-compress-images.php @@ -10,6 +10,7 @@ */ require dirname( __FILE__ ) . '/src/config/class-tiny-config.php'; +require dirname( __FILE__ ) . '/src/class-tiny-migrate.php'; require dirname( __FILE__ ) . '/src/class-tiny-helpers.php'; require dirname( __FILE__ ) . '/src/class-tiny-php.php'; require dirname( __FILE__ ) . '/src/class-tiny-wp-base.php'; @@ -37,6 +38,8 @@ require dirname( __FILE__ ) . '/src/class-tiny-compress-fopen.php'; } +Tiny_Migrate::run(); + $tiny_plugin = new Tiny_Plugin(); register_uninstall_hook( From 7cc6dde6f3dc8e9e2f41e5273033c36bc9f6f4f5 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 08:53:12 +0200 Subject: [PATCH 04/34] add migration tests --- test/helpers/wordpress.php | 7 +++- test/unit/TinyMigrateTest.php | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/unit/TinyMigrateTest.php diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index 45e2406d..a441056b 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -56,6 +56,7 @@ class WordPressStubs private $calls; private $stubs; private $filters; + public $postmeta = 'wp_postmeta'; public function __construct($vfs) { @@ -98,6 +99,7 @@ public function __construct($vfs) $this->addMethod('get_locale'); $this->addMethod('wp_timezone_string'); $this->addMethod('update_option'); + $this->addMethod('update'); $this->addMethod('check_ajax_referer'); $this->addMethod('wp_json_encode'); $this->addMethod('wp_send_json_error'); @@ -144,7 +146,10 @@ public function call($method, $args) } // Allow explicit stubs to override defaults/behaviors if (isset($this->stubs[$method]) && $this->stubs[$method]) { - return call_user_func_array($this->stubs[$method], $args); + if (is_callable($this->stubs[$method])) { + return call_user_func_array($this->stubs[$method], $args); + } + return $this->stubs[$method]; } if ('add_filter' === $method) { $tag = isset($args[0]) ? $args[0] : ''; diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php new file mode 100644 index 00000000..20f91983 --- /dev/null +++ b/test/unit/TinyMigrateTest.php @@ -0,0 +1,66 @@ +wp->postmeta = 'wp_postmeta'; + $this->wp->stub('update', 1); + } + + /** + * Helper to check if a specific option update occurred. + */ + private function assertOptionWasUpdated($option, $value) + { + $calls = $this->wp->getCalls('update_option'); + foreach ($calls as $call) { + if (($call[0] ?? null) === $option && ($call[1] ?? null) === $value) { + return $this->assertTrue(true); + } + } + $this->fail("Failed asserting that option '$option' was updated to '$value'."); + } + + public function test_run_skips_migration_when_db_version_is_current() + { + $this->wp->addOption(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); + + Tiny_Migrate::run(); + + $this->assertCount(0, $this->wp->getCalls('update'), 'Should not touch DB if version matches.'); + } + + public function test_run_performs_migration_and_updates_version() + { + Tiny_Migrate::run(); + + $update_calls = $this->wp->getCalls('update'); + $this->assertCount(1, $update_calls); + + list($table, $data, $where) = $update_calls[0]; + + $this->assertEquals('wp_postmeta', $table); + $this->assertEquals(['meta_key' => '_tiny_compress_images'], $data); + $this->assertEquals(['meta_key' => 'tiny_compress_images'], $where); + + $this->assertOptionWasUpdated(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); + } + + public function test_run_does_not_update_option_if_unnecessary() + { + $this->wp->addOption(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); + + Tiny_Migrate::run(); + + $option_calls = $this->wp->getCalls('update_option'); + $version_updates = array_filter($option_calls, fn($call) => $call[0] === Tiny_Migrate::DB_VERSION_OPTION); + + $this->assertEmpty($version_updates, 'Should not re-save the version if already current.'); + } +} From 662a5260e2e1c7a66d0fb93c758a5f9894b709a0 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 09:28:20 +0200 Subject: [PATCH 05/34] Change version to plain counter --- src/class-tiny-migrate.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 3c3940f5..7a2e8143 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -29,13 +29,15 @@ class Tiny_Migrate { /** - * The target version for the database. Not the same as plug-in version. - * This will be incremented on every new migration. + * The current database schema version. + * + * Increment this integer by 1 each time a new migration is added. + * This is independent of the plugin version. * * @since 3.7.0 - * @var string + * @var int */ - const DB_VERSION = '3.7.0'; + const DB_VERSION = 1; /** * WordPress option key used to track the applied database version. @@ -57,13 +59,13 @@ class Tiny_Migrate { * @return void */ public static function run() { - $stored_version = get_option( self::DB_VERSION_OPTION, '0' ); + $stored_version = (int) get_option( self::DB_VERSION_OPTION, 0 ); - if ( version_compare( $stored_version, self::DB_VERSION, '>=' ) ) { + if ( $stored_version >= self::DB_VERSION ) { return; } - if ( version_compare( $stored_version, '3.7.0', '<' ) ) { + if ( $stored_version < 1 ) { self::migrate_370(); } From ab4f772d23c989057f01fecda090a10a66fe40d4 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 09:29:11 +0200 Subject: [PATCH 06/34] Rename migration --- src/class-tiny-migrate.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 7a2e8143..1e313f2a 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -32,7 +32,6 @@ class Tiny_Migrate { * The current database schema version. * * Increment this integer by 1 each time a new migration is added. - * This is independent of the plugin version. * * @since 3.7.0 * @var int @@ -66,7 +65,7 @@ public static function run() { } if ( $stored_version < 1 ) { - self::migrate_370(); + self::migrate_meta_key_to_private(); } update_option( self::DB_VERSION_OPTION, self::DB_VERSION ); @@ -82,7 +81,7 @@ public static function run() { * * @return void */ - private static function migrate_370() { + private static function migrate_meta_key_to_private() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching From ace767f42665baf3d99c1dc7e559f86a836d8d45 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 09:29:50 +0200 Subject: [PATCH 07/34] exit if migration fails --- src/class-tiny-migrate.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 1e313f2a..43a59264 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -85,12 +85,16 @@ private static function migrate_meta_key_to_private() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching - $wpdb->update( + $result = $wpdb->update( $wpdb->postmeta, array( 'meta_key' => '_tiny_compress_images' ), array( 'meta_key' => 'tiny_compress_images' ), array( '%s' ), array( '%s' ) ); + + if ( false === $result ) { + return false; + } } } From d70b46c478f2457ae441217ccc4458263c25d2d1 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 09:57:49 +0200 Subject: [PATCH 08/34] consistent return value --- src/class-tiny-migrate.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 43a59264..bcf6016e 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -79,7 +79,7 @@ public static function run() { * * @since 3.7.0 * - * @return void + * @return boolean */ private static function migrate_meta_key_to_private() { global $wpdb; @@ -96,5 +96,7 @@ private static function migrate_meta_key_to_private() { if ( false === $result ) { return false; } + + return true; } } From 27bfcc7cea51097ad27eebdc63f4f4fee113013c Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 10:10:19 +0200 Subject: [PATCH 09/34] add changelog entry --- readme.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.txt b/readme.txt index c2b7dc6f..486c9306 100644 --- a/readme.txt +++ b/readme.txt @@ -174,6 +174,9 @@ A: You can upgrade to a paid account by adding your *Payment details* on your [a A: When the conversion feature is enabled (to convert images to AVIF or WebP), each image will use double the number of credits: one for compression and one for format conversion. == Changelog == += 3.7.0 = +* chore: migrated meta key from `tiny_compress_images` to `_tiny_compress_images` + = 3.6.14 = * fix: added check for valid path before deleting converted image * fix: use hook uninstall_plugin instead of uninstall.php to prevent dependency deletion From 5a5faaca8fc883071d5c2fdef76b5dfa5c72c742 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 10:19:15 +0200 Subject: [PATCH 10/34] cancel migration if it errored --- src/class-tiny-migrate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index bcf6016e..d3142559 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -64,8 +64,8 @@ public static function run() { return; } - if ( $stored_version < 1 ) { - self::migrate_meta_key_to_private(); + if ( $stored_version < 1 && ! self::migrate_meta_key_to_private() ) { + return; } update_option( self::DB_VERSION_OPTION, self::DB_VERSION ); From bde636885a4f05db93dd71c9a35179feb68c7431 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 10:19:23 +0200 Subject: [PATCH 11/34] remove redundent set --- test/unit/TinyMigrateTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 20f91983..8488bbab 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -9,7 +9,6 @@ class Tiny_Migrate_Test extends Tiny_TestCase public function set_up() { parent::set_up(); - $this->wp->postmeta = 'wp_postmeta'; $this->wp->stub('update', 1); } From 48f2c24f1c1b5d6ff66082b4219bc8c3cab2b255 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 11:14:32 +0200 Subject: [PATCH 12/34] move migration to plugins_loaded hook --- tiny-compress-images.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny-compress-images.php b/tiny-compress-images.php index a1412f47..8846f882 100644 --- a/tiny-compress-images.php +++ b/tiny-compress-images.php @@ -38,7 +38,7 @@ require dirname( __FILE__ ) . '/src/class-tiny-compress-fopen.php'; } -Tiny_Migrate::run(); +add_action( 'plugins_loaded', array( 'Tiny_Migrate', 'run' ) ); $tiny_plugin = new Tiny_Plugin(); From 669853ef6df550ee06d66b6b81bdc836b896f2e5 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:14:28 +0200 Subject: [PATCH 13/34] refer to the const instead of option --- src/class-tiny-migrate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index d3142559..b4fb4d44 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -22,7 +22,7 @@ * Handles sequential database migrations for the TinyPNG plugin. * * Each migration method targets a specific version and is only executed - * once per site, tracked via the `tinypng_db_version` option. + * once per site, tracked via the `DB_VERSION_OPTION` constant. * * @since 3.7.0 */ From da54abb30ec261910c319f5b38274192baa34123 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:17:45 +0200 Subject: [PATCH 14/34] comment for clarity where nothing to migration is positive --- src/class-tiny-migrate.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index b4fb4d44..e42d287a 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -97,6 +97,8 @@ private static function migrate_meta_key_to_private() { return false; } - return true; + // A return value of 0 means there was nothing to migrate, which is valid + // for fresh installs or databases that were already migrated. + return false !== $result;; } } From c53595891c1d7128a07436edfa9ce71eb8932deb Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:17:52 +0200 Subject: [PATCH 15/34] add test to validate migration failure --- test/unit/TinyMigrateTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 8488bbab..5b902d9e 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -51,6 +51,18 @@ public function test_run_performs_migration_and_updates_version() $this->assertOptionWasUpdated(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); } + public function test_run_does_not_update_db_version_when_migration_fails() + { + $this->wp->stub('update', function() { return false; }); + + Tiny_Migrate::run(); + + $option_calls = $this->wp->getCalls('update_option'); + $version_updates = array_filter($option_calls, fn($call) => $call[0] === Tiny_Migrate::DB_VERSION_OPTION); + + $this->assertEmpty($version_updates, 'Should not update DB version when migration fails.'); + } + public function test_run_does_not_update_option_if_unnecessary() { $this->wp->addOption(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); From 044ea9a15d0fe0f8929432261ea18bd7f852181c Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:29:55 +0200 Subject: [PATCH 16/34] create an order list of migrations --- src/class-tiny-migrate.php | 23 +++++++++++++++++++++-- test/unit/TinyMigrateTest.php | 10 +++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index e42d287a..f850ab30 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -46,6 +46,23 @@ class Tiny_Migrate { */ const DB_VERSION_OPTION = 'tinypng_db_version'; + /** + * Returns an ordered map of migrations keyed by version number. + * + * Each entry maps a version integer to a callable that performs the + * corresponding migration. Add new entries in ascending version order. + * Increment `DB_VERSION` when adding a new migration. + * + * @since 3.7.0 + * + * @return array Ordered map of version to migration callable. + */ + private static function migrations() { + return array( + 1 => array( self::class, 'migrate_meta_key_to_private' ), + ); + } + /** * Runs all pending migrations in version order. * @@ -64,8 +81,10 @@ public static function run() { return; } - if ( $stored_version < 1 && ! self::migrate_meta_key_to_private() ) { - return; + foreach ( self::migrations() as $version => $migration ) { + if ( $stored_version < $version && ! call_user_func( $migration ) ) { + return; + } } update_option( self::DB_VERSION_OPTION, self::DB_VERSION ); diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 5b902d9e..1f2c80d0 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -19,7 +19,7 @@ private function assertOptionWasUpdated($option, $value) { $calls = $this->wp->getCalls('update_option'); foreach ($calls as $call) { - if (($call[0] ?? null) === $option && ($call[1] ?? null) === $value) { + if (isset($call[0], $call[1]) && $call[0] === $option && $call[1] === $value) { return $this->assertTrue(true); } } @@ -45,8 +45,8 @@ public function test_run_performs_migration_and_updates_version() list($table, $data, $where) = $update_calls[0]; $this->assertEquals('wp_postmeta', $table); - $this->assertEquals(['meta_key' => '_tiny_compress_images'], $data); - $this->assertEquals(['meta_key' => 'tiny_compress_images'], $where); + $this->assertEquals(array('meta_key' => '_tiny_compress_images'), $data); + $this->assertEquals(array('meta_key' => 'tiny_compress_images'), $where); $this->assertOptionWasUpdated(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); } @@ -58,7 +58,7 @@ public function test_run_does_not_update_db_version_when_migration_fails() Tiny_Migrate::run(); $option_calls = $this->wp->getCalls('update_option'); - $version_updates = array_filter($option_calls, fn($call) => $call[0] === Tiny_Migrate::DB_VERSION_OPTION); + $version_updates = array_filter($option_calls, function($call) { return $call[0] === Tiny_Migrate::DB_VERSION_OPTION; }); $this->assertEmpty($version_updates, 'Should not update DB version when migration fails.'); } @@ -70,7 +70,7 @@ public function test_run_does_not_update_option_if_unnecessary() Tiny_Migrate::run(); $option_calls = $this->wp->getCalls('update_option'); - $version_updates = array_filter($option_calls, fn($call) => $call[0] === Tiny_Migrate::DB_VERSION_OPTION); + $version_updates = array_filter($option_calls, function($call) { return $call[0] === Tiny_Migrate::DB_VERSION_OPTION; }); $this->assertEmpty($version_updates, 'Should not re-save the version if already current.'); } From 9a75b4bd74b3866516c6d0bdf2e6270c4ee51542 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:44:09 +0200 Subject: [PATCH 17/34] log error when migration failed --- src/class-tiny-migrate.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index f850ab30..e2becdc9 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -82,7 +82,11 @@ public static function run() { } foreach ( self::migrations() as $version => $migration ) { - if ( $stored_version < $version && ! call_user_func( $migration ) ) { + if ( $stored_version >= $version ) { + continue; + } + + if ( ! call_user_func( $migration ) ) { return; } } @@ -98,7 +102,7 @@ public static function run() { * * @since 3.7.0 * - * @return boolean + * @return bool True on success or when there is nothing to migrate, false on DB error. */ private static function migrate_meta_key_to_private() { global $wpdb; @@ -113,11 +117,13 @@ private static function migrate_meta_key_to_private() { ); if ( false === $result ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + error_log( 'Tinify: failed to migrate meta key. DB error: ' . $wpdb->last_error ); return false; } // A return value of 0 means there was nothing to migrate, which is valid - // for fresh installs or databases that were already migrated. - return false !== $result;; + // for fresh installs or databases that were already migrated. + return false !== $result; } } From 454ad776c60ca8340f5132c4295faeaf6faf1674 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:50:10 +0200 Subject: [PATCH 18/34] add backoff mechanism to retry migration each hour --- src/class-tiny-migrate.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index e2becdc9..c9304b6d 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -46,6 +46,15 @@ class Tiny_Migrate { */ const DB_VERSION_OPTION = 'tinypng_db_version'; + /** + * When migration fails, will pause migration for an hour + * when the key exists in memory + * + * @since 3.7.0 + * @var string + */ + const MIGRATION_BACKOFF_KEY = 'tinypng_migration_backoff'; + /** * Returns an ordered map of migrations keyed by version number. * @@ -86,7 +95,14 @@ public static function run() { continue; } + if ( get_transient( self::MIGRATION_BACKOFF_KEY ) ) { + // transient key to hold migrations exists so exit early + return; + } + + if ( ! call_user_func( $migration ) ) { + set_transient( self::MIGRATION_BACKOFF_KEY, 1, HOUR_IN_SECONDS ); return; } } From 54027e05fd8c21d894b150a6daf5c3ef6af578f2 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 13:50:26 +0200 Subject: [PATCH 19/34] format --- src/class-tiny-migrate.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index c9304b6d..093d68d7 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -49,7 +49,7 @@ class Tiny_Migrate { /** * When migration fails, will pause migration for an hour * when the key exists in memory - * + * * @since 3.7.0 * @var string */ @@ -100,7 +100,6 @@ public static function run() { return; } - if ( ! call_user_func( $migration ) ) { set_transient( self::MIGRATION_BACKOFF_KEY, 1, HOUR_IN_SECONDS ); return; From 1029b73c79ba75e268905443f41912a6ee62be11 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 14:07:04 +0200 Subject: [PATCH 20/34] add tests for backoff --- test/helpers/wordpress.php | 24 ++++++++++++++++++++++++ test/unit/TinyMigrateTest.php | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index a441056b..02f546ba 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -2,6 +2,7 @@ define('ABSPATH', dirname(__FILE__) . '/../'); define('WPINC', 'wp-includes-for-tests'); +define('HOUR_IN_SECONDS', 3600); require_once dirname(__FILE__) . '/../' . WPINC . '/file.php'; use org\bovigo\vfs\vfsStream; @@ -53,10 +54,12 @@ class WordPressStubs private $admin_initFunctions; private $options; private $metadata; + private $transients; private $calls; private $stubs; private $filters; public $postmeta = 'wp_postmeta'; + public $last_error = ''; public function __construct($vfs) { @@ -100,6 +103,9 @@ public function __construct($vfs) $this->addMethod('wp_timezone_string'); $this->addMethod('update_option'); $this->addMethod('update'); + $this->addMethod('get_transient'); + $this->addMethod('set_transient'); + $this->addMethod('delete_transient'); $this->addMethod('check_ajax_referer'); $this->addMethod('wp_json_encode'); $this->addMethod('wp_send_json_error'); @@ -124,6 +130,7 @@ public function defaults() $this->admin_initFunctions = array(); $this->options = new WordPressOptions(); $this->metadata = array(); + $this->transients = array(); $this->filters = array(); $GLOBALS['_wp_additional_image_sizes'] = array(); } @@ -190,6 +197,18 @@ public function call($method, $args) } if ('translate' === $method) { return $args[0]; + } elseif ('get_transient' === $method) { + $key = isset($args[0]) ? $args[0] : ''; + return isset($this->transients[$key]) ? $this->transients[$key] : false; + } elseif ('set_transient' === $method) { + $key = isset($args[0]) ? $args[0] : ''; + $value = isset($args[1]) ? $args[1] : ''; + $this->transients[$key] = $value; + return true; + } elseif ('delete_transient' === $method) { + $key = isset($args[0]) ? $args[0] : ''; + unset($this->transients[$key]); + return true; } elseif ('get_option' === $method) { return call_user_func_array(array($this->options, 'get'), $args); } elseif ('get_post_meta' === $method) { @@ -229,6 +248,11 @@ public function addOption($key, $value) $this->options->set($key, $value); } + public function addTransient($key, $value) + { + $this->transients[$key] = $value; + } + public function addImageSize($size, $values) { $GLOBALS['_wp_additional_image_sizes'][$size] = $values; diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 1f2c80d0..937df2b4 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -74,4 +74,27 @@ public function test_run_does_not_update_option_if_unnecessary() $this->assertEmpty($version_updates, 'Should not re-save the version if already current.'); } + + public function test_run_sets_backoff_transient_when_migration_fails() + { + $this->wp->stub('update', function() { return false; }); + + Tiny_Migrate::run(); + + $set_transient_calls = $this->wp->getCalls('set_transient'); + $this->assertCount(1, $set_transient_calls, 'A backoff transient should be set after a failed migration.'); + $this->assertEquals(Tiny_Migrate::MIGRATION_BACKOFF_KEY, $set_transient_calls[0][0]); + $this->assertEquals(HOUR_IN_SECONDS, $set_transient_calls[0][2]); + } + + public function test_run_skips_migration_when_backoff_transient_is_set() + { + $this->wp->stub('get_transient', function($key) { + return Tiny_Migrate::MIGRATION_BACKOFF_KEY === $key ? 1 : false; + }); + + Tiny_Migrate::run(); + + $this->assertCount(0, $this->wp->getCalls('update'), 'DB update should not be attempted during the backoff period.'); + } } From 4c0f7fa80a52464b5cc34dd209a1bc4ed91111a6 Mon Sep 17 00:00:00 2001 From: tijmen Date: Fri, 15 May 2026 14:37:43 +0200 Subject: [PATCH 21/34] only add callable methods to stubs, swallow error logs in test --- test/helpers/wordpress.php | 5 +---- test/unit/TinyMigrateTest.php | 11 ++++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index 02f546ba..f18543a6 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -153,10 +153,7 @@ public function call($method, $args) } // Allow explicit stubs to override defaults/behaviors if (isset($this->stubs[$method]) && $this->stubs[$method]) { - if (is_callable($this->stubs[$method])) { - return call_user_func_array($this->stubs[$method], $args); - } - return $this->stubs[$method]; + return call_user_func_array($this->stubs[$method], $args); } if ('add_filter' === $method) { $tag = isset($args[0]) ? $args[0] : ''; diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 937df2b4..491a8543 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -9,7 +9,11 @@ class Tiny_Migrate_Test extends Tiny_TestCase public function set_up() { parent::set_up(); - $this->wp->stub('update', 1); + // migration test logs error in stdout so swallow error logs + ini_set('error_log', '/dev/null'); + $this->wp->stub('update', function() { + return 1; + }); } /** @@ -69,10 +73,7 @@ public function test_run_does_not_update_option_if_unnecessary() Tiny_Migrate::run(); - $option_calls = $this->wp->getCalls('update_option'); - $version_updates = array_filter($option_calls, function($call) { return $call[0] === Tiny_Migrate::DB_VERSION_OPTION; }); - - $this->assertEmpty($version_updates, 'Should not re-save the version if already current.'); + $this->assertEmpty($this->wp->getCalls('update_option'), 'Should not call update_option at all when version is already current.'); } public function test_run_sets_backoff_transient_when_migration_fails() From d2641ac1e5c3dd90afa04f40477ee0f20391ddea Mon Sep 17 00:00:00 2001 From: tijmen Date: Tue, 9 Jun 2026 16:22:35 +0200 Subject: [PATCH 22/34] change docs --- src/class-tiny-migrate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 093d68d7..16f462e1 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -48,7 +48,7 @@ class Tiny_Migrate { /** * When migration fails, will pause migration for an hour - * when the key exists in memory + * as long as the key exists in transient * * @since 3.7.0 * @var string From c5073f3085d5f0ee82db884a17a3eff58e7bfc17 Mon Sep 17 00:00:00 2001 From: tijmen Date: Tue, 9 Jun 2026 16:34:51 +0200 Subject: [PATCH 23/34] add wp_cache_flush to prevent stale meta data keys --- src/class-tiny-migrate.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 16f462e1..e40ba9fc 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -137,6 +137,8 @@ private static function migrate_meta_key_to_private() { return false; } + wp_cache_flush(); + // A return value of 0 means there was nothing to migrate, which is valid // for fresh installs or databases that were already migrated. return false !== $result; From bc3c83da1ffb4c336534ede9475f113e053c2122 Mon Sep 17 00:00:00 2001 From: tijmen Date: Tue, 9 Jun 2026 17:06:21 +0200 Subject: [PATCH 24/34] migrate in batches of 2500 --- src/class-tiny-migrate.php | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index e40ba9fc..966bac57 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -122,25 +122,26 @@ public static function run() { private static function migrate_meta_key_to_private() { global $wpdb; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching - $result = $wpdb->update( - $wpdb->postmeta, - array( 'meta_key' => '_tiny_compress_images' ), - array( 'meta_key' => 'tiny_compress_images' ), - array( '%s' ), - array( '%s' ) - ); - - if ( false === $result ) { - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - error_log( 'Tinify: failed to migrate meta key. DB error: ' . $wpdb->last_error ); - return false; - } - + $batch_size = 2500; + do { + $query = + "UPDATE $wpdb->postmeta + SET meta_key = '_tiny_compress_images' + WHERE meta_key = 'tiny_compress_images' + LIMIT " . $batch_size; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Renames a fixed internal meta key in fixed-size batches; only internal constants are interpolated. + $result = $wpdb->query( $query ); + + if ( false === $result ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + error_log( 'Tinify: failed to migrate meta key. DB error: ' . $wpdb->last_error ); + return false; + } + } while ( $batch_size === (int) $result ); + wp_cache_flush(); - // A return value of 0 means there was nothing to migrate, which is valid - // for fresh installs or databases that were already migrated. - return false !== $result; + return true; } } From 3404849fe5ce1b257dcf45ed9a925a4425a90faa Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:22:54 +0200 Subject: [PATCH 25/34] fall back to legacy key when migrating --- src/class-tiny-image.php | 23 +++++++- src/config/class-tiny-config.php | 1 + test/helpers/wordpress.php | 2 + test/unit/TinyMigrateTest.php | 93 ++++++++++++++++++++++++++++---- 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/src/class-tiny-image.php b/src/class-tiny-image.php index a586f430..844b1033 100644 --- a/src/class-tiny-image.php +++ b/src/class-tiny-image.php @@ -139,9 +139,30 @@ private function duplicate_check( $filenames, $file, $size_name ) { return $filenames; } + /** + * Will retrieve compression meta data for the given post_id. + * + * As migrations on large libraries can take longer, we will fall back on + * the legacy key on migrating. We can remove the LEGACY_META_KEY on 3.8.0. + * + * @since 3.7.0 + * + * @param int $post_id Attachment ID. + * @return mixed The stored tiny metadata, or '' when none exists. + */ + public static function get_tiny_metadata( $post_id ) { + $tiny_metadata = get_post_meta( $post_id, Tiny_Config::META_KEY, true ); + + if ( empty( $tiny_metadata ) ) { + $tiny_metadata = get_post_meta( $post_id, Tiny_Config::LEGACY_META_KEY, true ); + } + + return $tiny_metadata; + } + private function parse_tiny_metadata( $tiny_metadata = null ) { if ( is_null( $tiny_metadata ) ) { - $tiny_metadata = get_post_meta( $this->id, Tiny_Config::META_KEY, true ); + $tiny_metadata = Tiny_Image::get_tiny_metadata( $this->id ); } if ( $tiny_metadata ) { foreach ( $tiny_metadata as $size => $meta ) { diff --git a/src/config/class-tiny-config.php b/src/config/class-tiny-config.php index 6378578c..5b3aa717 100644 --- a/src/config/class-tiny-config.php +++ b/src/config/class-tiny-config.php @@ -10,4 +10,5 @@ class Tiny_Config { const KEYS_URL = 'https://api.tinify.com/keys'; const MONTHLY_FREE_COMPRESSIONS = 500; const META_KEY = '_tiny_compress_images'; + const LEGACY_META_KEY = 'tiny_compress_images'; } diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index f18543a6..ac436a25 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -103,6 +103,8 @@ public function __construct($vfs) $this->addMethod('wp_timezone_string'); $this->addMethod('update_option'); $this->addMethod('update'); + $this->addMethod('query'); + $this->addMethod('wp_cache_flush'); $this->addMethod('get_transient'); $this->addMethod('set_transient'); $this->addMethod('delete_transient'); diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 491a8543..7850ee39 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -11,11 +11,25 @@ public function set_up() parent::set_up(); // migration test logs error in stdout so swallow error logs ini_set('error_log', '/dev/null'); - $this->wp->stub('update', function() { + $this->wp->stub('query', function() { return 1; }); } + /** + * Stubs $wpdb->query to return the given row counts on successive calls, + * simulating the batched UPDATE draining the table. + */ + private function queueQueryResults(array $results) + { + $index = 0; + $this->wp->stub('query', function() use (&$index, $results) { + $value = isset($results[$index]) ? $results[$index] : 0; + $index++; + return $value; + }); + } + /** * Helper to check if a specific option update occurred. */ @@ -36,28 +50,85 @@ public function test_run_skips_migration_when_db_version_is_current() Tiny_Migrate::run(); - $this->assertCount(0, $this->wp->getCalls('update'), 'Should not touch DB if version matches.'); + $this->assertCount(0, $this->wp->getCalls('query'), 'Should not touch DB if version matches.'); } public function test_run_performs_migration_and_updates_version() { Tiny_Migrate::run(); - $update_calls = $this->wp->getCalls('update'); - $this->assertCount(1, $update_calls); + $query_calls = $this->wp->getCalls('query'); + $this->assertCount(1, $query_calls); + + $sql = $query_calls[0][0]; + + $this->assertStringContainsString('UPDATE wp_postmeta', $sql); + $this->assertStringContainsString("SET meta_key = '_tiny_compress_images'", $sql); + $this->assertStringContainsString("WHERE meta_key = 'tiny_compress_images'", $sql); + $this->assertStringContainsString('LIMIT ' . 2500, $sql); + + $this->assertOptionWasUpdated(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); + } - list($table, $data, $where) = $update_calls[0]; + public function test_run_renames_meta_key_in_batches() + { + // Two full batches (2500 rows) followed by a partial batch end the loop. + $this->queueQueryResults(array(2500, 2500, 42)); - $this->assertEquals('wp_postmeta', $table); - $this->assertEquals(array('meta_key' => '_tiny_compress_images'), $data); - $this->assertEquals(array('meta_key' => 'tiny_compress_images'), $where); + Tiny_Migrate::run(); + $this->assertCount(3, $this->wp->getCalls('query'), 'Should keep batching until a partial batch is returned.'); $this->assertOptionWasUpdated(Tiny_Migrate::DB_VERSION_OPTION, Tiny_Migrate::DB_VERSION); } + public function test_get_tiny_metadata_reads_the_current_private_key() + { + $this->wp->updateMetadata(1, Tiny_Config::META_KEY, array('size' => 'current')); + + $this->assertEquals(array('size' => 'current'), Tiny_Migrate::get_tiny_metadata(1)); + } + + public function test_get_tiny_metadata_falls_back_to_legacy_key_before_migration() + { + // Data still lives under the old public key because the migration has + // not run yet (or is still in flight / backed off). + $this->wp->updateMetadata(1, Tiny_Config::LEGACY_META_KEY, array('size' => 'legacy')); + + $this->assertEquals(array('size' => 'legacy'), Tiny_Migrate::get_tiny_metadata(1)); + } + + public function test_get_tiny_metadata_prefers_current_key_over_legacy() + { + $this->wp->updateMetadata(1, Tiny_Config::LEGACY_META_KEY, array('size' => 'legacy')); + $this->wp->updateMetadata(1, Tiny_Config::META_KEY, array('size' => 'current')); + + $this->assertEquals(array('size' => 'current'), Tiny_Migrate::get_tiny_metadata(1)); + } + + public function test_get_tiny_metadata_returns_empty_when_no_metadata_exists() + { + $this->assertEmpty(Tiny_Migrate::get_tiny_metadata(1)); + } + + public function test_run_flushes_object_cache_after_migrating() + { + Tiny_Migrate::run(); + + $this->assertCount(1, $this->wp->getCalls('wp_cache_flush'), 'Object cache must be flushed so reads see the renamed key.'); + } + + public function test_run_does_not_flush_cache_when_migration_fails() + { + $this->wp->stub('query', function() { return false; }); + + Tiny_Migrate::run(); + + $this->assertCount(0, $this->wp->getCalls('wp_cache_flush')); + } + public function test_run_does_not_update_db_version_when_migration_fails() { - $this->wp->stub('update', function() { return false; }); + $this->wp->stub('query', function() { return false; }); Tiny_Migrate::run(); @@ -78,7 +149,7 @@ public function test_run_does_not_update_option_if_unnecessary() public function test_run_sets_backoff_transient_when_migration_fails() { - $this->wp->stub('update', function() { return false; }); + $this->wp->stub('query', function() { return false; }); Tiny_Migrate::run(); @@ -96,6 +167,6 @@ public function test_run_skips_migration_when_backoff_transient_is_set() Tiny_Migrate::run(); - $this->assertCount(0, $this->wp->getCalls('update'), 'DB update should not be attempted during the backoff period.'); + $this->assertCount(0, $this->wp->getCalls('query'), 'DB update should not be attempted during the backoff period.'); } } From dbf6aea2f2daf2cf6a4518e97d782ceab21d42ce Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:25:07 +0200 Subject: [PATCH 26/34] require php 5.6 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 35a13517..974a5a78 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ } ], "require": { - "php": ">=5.3.0", + "php": ">=5.6.0", "composer/installers": "~1.0" }, "require-dev": { From 2c94ac84221429837b57778e0bb6e0a84bc3fa9c Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:30:05 +0200 Subject: [PATCH 27/34] define when undefined --- test/helpers/wordpress.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index ac436a25..da5f2935 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -2,7 +2,9 @@ define('ABSPATH', dirname(__FILE__) . '/../'); define('WPINC', 'wp-includes-for-tests'); -define('HOUR_IN_SECONDS', 3600); +if (! defined('HOUR_IN_SECONDS')) { + define('HOUR_IN_SECONDS', 3600); +} require_once dirname(__FILE__) . '/../' . WPINC . '/file.php'; use org\bovigo\vfs\vfsStream; From b602bdc551d17ca34e69d7aa5ded3d30865518a4 Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:30:30 +0200 Subject: [PATCH 28/34] format --- src/class-tiny-image.php | 2 +- src/class-tiny-migrate.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class-tiny-image.php b/src/class-tiny-image.php index 844b1033..41a713a3 100644 --- a/src/class-tiny-image.php +++ b/src/class-tiny-image.php @@ -141,7 +141,7 @@ private function duplicate_check( $filenames, $file, $size_name ) { /** * Will retrieve compression meta data for the given post_id. - * + * * As migrations on large libraries can take longer, we will fall back on * the legacy key on migrating. We can remove the LEGACY_META_KEY on 3.8.0. * diff --git a/src/class-tiny-migrate.php b/src/class-tiny-migrate.php index 966bac57..de38aae1 100644 --- a/src/class-tiny-migrate.php +++ b/src/class-tiny-migrate.php @@ -139,7 +139,7 @@ private static function migrate_meta_key_to_private() { return false; } } while ( $batch_size === (int) $result ); - + wp_cache_flush(); return true; From 7e2551dbd92aeeaf3c3d16f971d39992278945b9 Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:32:08 +0200 Subject: [PATCH 29/34] stop swallwing errs --- test/unit/TinyMigrateTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 7850ee39..69bd122b 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -9,8 +9,6 @@ class Tiny_Migrate_Test extends Tiny_TestCase public function set_up() { parent::set_up(); - // migration test logs error in stdout so swallow error logs - ini_set('error_log', '/dev/null'); $this->wp->stub('query', function() { return 1; }); From 4786c2ef43616a775543862b2fa5d6fec1cb815d Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:33:29 +0200 Subject: [PATCH 30/34] use self as it is within class --- src/class-tiny-image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class-tiny-image.php b/src/class-tiny-image.php index 41a713a3..d927e2c2 100644 --- a/src/class-tiny-image.php +++ b/src/class-tiny-image.php @@ -162,7 +162,7 @@ public static function get_tiny_metadata( $post_id ) { private function parse_tiny_metadata( $tiny_metadata = null ) { if ( is_null( $tiny_metadata ) ) { - $tiny_metadata = Tiny_Image::get_tiny_metadata( $this->id ); + $tiny_metadata = self::get_tiny_metadata( $this->id ); } if ( $tiny_metadata ) { foreach ( $tiny_metadata as $size => $meta ) { From 7538b0173aac06ca8a1723533d2eb1fdeb3509d2 Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:42:40 +0200 Subject: [PATCH 31/34] revert php require --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 974a5a78..35a13517 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ } ], "require": { - "php": ">=5.6.0", + "php": ">=5.3.0", "composer/installers": "~1.0" }, "require-dev": { From 60415a815ec5ae4f18ccfc2e5f01311fc6dfee0f Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 06:45:20 +0200 Subject: [PATCH 32/34] fix tests after moving func --- test/unit/TinyMigrateTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/TinyMigrateTest.php b/test/unit/TinyMigrateTest.php index 69bd122b..146c40e6 100644 --- a/test/unit/TinyMigrateTest.php +++ b/test/unit/TinyMigrateTest.php @@ -83,7 +83,7 @@ public function test_get_tiny_metadata_reads_the_current_private_key() { $this->wp->updateMetadata(1, Tiny_Config::META_KEY, array('size' => 'current')); - $this->assertEquals(array('size' => 'current'), Tiny_Migrate::get_tiny_metadata(1)); + $this->assertEquals(array('size' => 'current'), Tiny_Image::get_tiny_metadata(1)); } public function test_get_tiny_metadata_falls_back_to_legacy_key_before_migration() @@ -92,7 +92,7 @@ public function test_get_tiny_metadata_falls_back_to_legacy_key_before_migration // not run yet (or is still in flight / backed off). $this->wp->updateMetadata(1, Tiny_Config::LEGACY_META_KEY, array('size' => 'legacy')); - $this->assertEquals(array('size' => 'legacy'), Tiny_Migrate::get_tiny_metadata(1)); + $this->assertEquals(array('size' => 'legacy'), Tiny_Image::get_tiny_metadata(1)); } public function test_get_tiny_metadata_prefers_current_key_over_legacy() @@ -100,12 +100,12 @@ public function test_get_tiny_metadata_prefers_current_key_over_legacy() $this->wp->updateMetadata(1, Tiny_Config::LEGACY_META_KEY, array('size' => 'legacy')); $this->wp->updateMetadata(1, Tiny_Config::META_KEY, array('size' => 'current')); - $this->assertEquals(array('size' => 'current'), Tiny_Migrate::get_tiny_metadata(1)); + $this->assertEquals(array('size' => 'current'), Tiny_Image::get_tiny_metadata(1)); } public function test_get_tiny_metadata_returns_empty_when_no_metadata_exists() { - $this->assertEmpty(Tiny_Migrate::get_tiny_metadata(1)); + $this->assertEmpty(Tiny_Image::get_tiny_metadata(1)); } public function test_run_flushes_object_cache_after_migrating() From e51471485e2dce81bebdd24ba651b2fc90f06858 Mon Sep 17 00:00:00 2001 From: tijmen Date: Mon, 22 Jun 2026 11:54:30 +0200 Subject: [PATCH 33/34] add legacy key to tests --- test/fixtures/class-tiny-config.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/fixtures/class-tiny-config.php b/test/fixtures/class-tiny-config.php index 4c748efb..b17c5360 100644 --- a/test/fixtures/class-tiny-config.php +++ b/test/fixtures/class-tiny-config.php @@ -1,18 +1,19 @@ Date: Mon, 22 Jun 2026 12:13:17 +0200 Subject: [PATCH 34/34] run migration on admin_init --- tiny-compress-images.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny-compress-images.php b/tiny-compress-images.php index 8846f882..567894db 100644 --- a/tiny-compress-images.php +++ b/tiny-compress-images.php @@ -38,7 +38,7 @@ require dirname( __FILE__ ) . '/src/class-tiny-compress-fopen.php'; } -add_action( 'plugins_loaded', array( 'Tiny_Migrate', 'run' ) ); +add_action( 'admin_init', array( 'Tiny_Migrate', 'run' ) ); $tiny_plugin = new Tiny_Plugin();