fix: bump the database version only after the migration succeeded - #844
Open
2ndkauboy wants to merge 1 commit into
Open
fix: bump the database version only after the migration succeeded#8442ndkauboy wants to merge 1 commit into
2ndkauboy wants to merge 1 commit into
Conversation
🔍 WordPress Plugin Check Report
📊 Report
❌ Errors (1)📁 readme.txt (1 error)
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
`Handlers\PluginUpdate::maybe_update_database()` wrote the new database version before it migrated anything, so a single failure spent the one chance a site gets. Any error inside the steps — a fatal, a DB error, a timeout, a warning promoted to an exception — left the version already raised, `db_version_is_current()` reported the database as up-to-date on every later request, and the migration never ran again. The consequence is silent and unrecoverable: the legacy `antispam_bee` option is still there, `antispam_bee_options` was never written, and the site falls back to `Settings::$defaults`. The user's entire configuration is gone with no way to retrigger the migration short of editing `antispambee_db_version` by hand. The version write now happens after the steps have completed. Deferring it cannot make the migration run twice inside one request, because `self::$db_update_triggered` is set before any of it. The steps move into `run_migration_steps()`, reached through `static::` so a test can supply a failing migration without needing an error the suite cannot provoke on purpose. The `null` check becomes `is_scalar()` on the way: a fresh install has nothing to migrate, and a recorded revision that is not a scalar cannot be compared against — which used to fatal once and would now fatal on every request, since nothing raises the version past it. Fixes #798
2ndkauboy
force-pushed
the
fix/798-db-version-after-migration
branch
from
August 21, 2026 20:45
61e0312 to
08bdb81
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #798.
Handlers\PluginUpdate::maybe_update_database()wrote the new database version before it migrated anything, so any error inside the steps spent the one chance a site gets.db_version_is_current()then reported the database as up-to-date on every later request and the migration never ran again.What the user sees: the legacy
antispam_beeoption is still in the database,antispam_bee_optionswas never written, and the site falls back toSettings::$defaults. Their entire configuration is gone, silently, with no way to retrigger the migration short of editingantispambee_db_versionby hand.#793 removed the most likely trigger — the
Undefined array keywarnings — but not the failure mode. A fatal, a DB error, a timeout, or anything a future step adds still burns the one attempt.The change
The
update_option()call moves after the steps. Deferring it cannot make the migration run twice inside one request:self::$db_update_triggeredis set before any of it, which is what the issue already noted.The steps move into
run_migration_steps(), reached throughstatic::rather thanself::. That is what lets a test supply a failing migration — the suite cannot provoke a real fatal on purpose, and defining a constant to force one leaks into every later test in the process.One thing worth reviewing
The
null === $version_from_dbcheck becameis_scalar( $version_from_db ).A fresh install has nothing to migrate, which
nullalready covered. But a recorded revision that is not a scalar cannot be compared against at all, and with the version write no longer happening first, such a value would now fatal on every request rather than once. Both cases have no migration to run, so both fall through to the version write. Only this class ever writes that option, so this is defence against a corrupted row rather than a case anyone should hit.Tests
Two integration tests, in
tests/Integration/Handlers/PluginUpdateTest.php:1.02and writes no v3 optionBoth fail against the old code — verified by reinstating the early write:
The failure is driven through
FailingPluginUpdate, a subclass whoserun_migration_steps()throws, standing in for a fatal, a DB error, a timeout or a promoted warning.PHPStan clean,
phpcsclean.