[3.0] Removes the settings that updateModSettings() is asked to remove - #9532
Conversation
Passing null as a value is how a setting is deleted, and it never deleted
anything. array_filter() keeps the keys, so $to_remove is a list of names
mapped to nulls, and {array_string:remove} binds the values -- so the
statement that ran was:
DELETE FROM smf_settings WHERE variable IN ('')
which matches nothing and reports no error. The names are what the placeholder
wants, and array_keys($change_array, null, true) is a shorter way of finding
them than filtering and then taking the keys.
Two more places had to lose them or the setting comes straight back. The copy
in Config::$modSettings was never touched, so the removed setting was still
readable for the rest of the request, while every other path through this
method keeps that copy in step. And a call that only removes settings returns
before either of the existing cache invalidations, so with caching on the next
request read the setting back out of the cache.
The callers are three migrations whose entire job is to drop a setting --
v2_1\SettingsUpdate with 22 of them, v3_0\RemoveCookieTime, and
v3_0\LanguageDirectory renaming one -- plus Search\APIs\Parsed clearing its
status. None of them have ever worked. Upgrading a 2.1 forum leaves time_offset
behind in smf_settings, which is what led here.
Fixes SimpleMachines#9530
Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
|
Confirmed end to end, rather than left as an inference from the unit-level checks. A real 2.1 → 3.0 upgrade of the committed 2.1.7 baseline from #9330, run twice on MySQL 8.4 against the same dump — once with this branch and once with Reading
So two settings that a migration exists specifically to delete survive an upgrade today, and both go when this lands. The rest are absent from both runs for the reason given in the description: a forum installed as 2.1 never writes them. They are 2.0-era settings, so the 2.0 → 3.0 path is where the remaining twenty would show, and I have no 2.0 baseline to run that against. Nothing else moved. The schema comparison either side of the upgrade is identical between the two runs — 55 differences in the schema and 27 outside it, the same list both times — so this changes what is in |
Description
Passing
nullas a value is howConfig::updateModSettings()is asked to delete a setting, and it never deleted anything.array_filter()keeps the keys, so$to_removeends up as a list of names mapped to nulls, and{array_string:remove}binds the values. The statement that actually ran was:It matches nothing, succeeds, and reports no error. The names are what the placeholder wants, and
array_keys($change_array, null, true)finds them in one step.Two other places had to lose the setting as well, or it comes straight back:
Config::$modSettingswas never updated. Every other path through this method keeps that copy in step with the database; the removal path did not, so a setting stayed readable for the rest of the request after being deleted.CacheApi::put('modSettings', null, 90), but when$change_arrayholds nothing but nulls it is empty by the time the replace path is reached, so it returns at theempty($replace_array)check first. With caching on, the next request read the setting back out of the cache.Where it came from, and 2.1
The deletion worked until 1a8e2b3ac ("Reduces redundant code in SMF\Config", 19 May 2026), which replaced the loop that collected the names with
array_filter(). Same intent, tidier, butarray_filter()preserves keys, so a list of names became a map of names to nulls while the placeholder went on binding the values.2.1 is affected in a lighter form.
updateSettings()there builds$toRemove[] = $k, so the delete itself is correct and this half of the bug is 3.0 only. The other two halves are shared: it does not unset the removed setting from$modSettingseither, and it returns atif (empty($replaceArray)) return;beforecache_put_data(), so a removal-only call leaves the cache holding the setting. Both are latent there — nothing in 2.1 core passesnulltoupdateSettings(), so only a mod would reach them — which is why this is proposed against 3.0 alone.Who this affects
Three migrations whose entire job is to drop a setting, plus one search API:
v2_1\SettingsUpdate$removedSettingsv3_0\RemoveCookieTimecookieTimev3_0\LanguageDirectorySearch\APIs\Parsedsearch_parsed_statusNone of them have ever worked. Upgrading a 2.1 forum leaves
time_offsetsitting insmf_settingsafterwards, which is what led here.time_offsetis the visible one only because a 2.1 install never writes the other 21 — they are 2.0-era settings, so a 2.0 → 3.0 upgrade is where the rest of them survive.Testing
Ten checks against a fresh 3.0 install, on both engines, with file-based caching enabled so the cache half is exercised:
MySQL 8.4 and PostgreSQL 17 both. The same ten against
release-3.0unchanged give 5 passed, 5 failed — every failure one of the three symptoms above, so none of the new checks are decoration.php-cs-fixerclean.Issues References (Fixes|Related|Closes)
Fixes #9530