Skip to content

fix(settings): stop reading Dokan settings before init - #3346

Open
kzamanbd wants to merge 1 commit into
refactor/simplify-settings-to-flat-arrayfrom
fix/settings-textdomain-loaded-too-early
Open

fix(settings): stop reading Dokan settings before init#3346
kzamanbd wants to merge 1 commit into
refactor/simplify-settings-to-flat-arrayfrom
fix/settings-textdomain-loaded-too-early

Conversation

@kzamanbd

Copy link
Copy Markdown
Contributor

All Submissions:

  • My code follow the WordPress' coding standards
  • My code satisfies feature requirements
  • My code is tested
  • My code passes the PHPCS tests
  • My code has proper inline documentation
  • I've included related pull request(s) (optional)
  • I've included developer documentation (optional)
  • I've added proper labels to this pull request

Changes proposed in this Pull Request:

Every request on this branch logs the WordPress 6.7 doing-it-wrong notice twice:

PHP Notice:  Function _load_textdomain_just_in_time was called incorrectly.
Translation loading for the dokan-lite domain was triggered too early. ...

A doing_it_wrong_run backtrace pins it to one chain:

FullWidthVendorLayout::register_hooks()   @ plugins_loaded  (dokan-class.php:278)
 → dokan_get_option()                     functions.php:1077
 → LegacySettingsRepository::all()
 → LegacySettingsBridge::hydrate_legacy_from_new() → build_map() → harvest_from_schema()
 → SettingsSchema::get_schema() → esc_html__( '…', 'dokan-lite' )   ← before init

On this branch, reading any Dokan setting walks the legacy settings bridge, and the bridge harvests its legacy-key mapping out of SettingsSchema::get_schema() — a ~2,800-line schema full of esc_html__() calls. register_hooks() runs on plugins_loaded, but load_plugin_textdomain() is registered on init (dokan-class.php:263), so the translation calls land too early.

BridgeBootstrap already defers its own harvest to init priority 999 for exactly this reason; these two call sites had not been given the same treatment.

1. FullWidthVendorLayout — the trigger.

The three layout hooks are now registered unconditionally, and the vendor_layout_style check moved into the callbacks behind a new is_latest_layout() helper. All three fire on init or later, so the read is safe there, and the repository memoizes the section snapshot so deferring it costs nothing. Externally observable behaviour is unchanged — a legacy-layout marketplace still gets no assets and no template override.

2. LegacySettingsRepository::__construct() — a second defect found on the way.

The constructor called known_sections() purely to learn which legacy dokan_* options to bind cache-flush listeners to. That forced a full schema build during construction, on every front-end request that read a single option. It was also wrong: the map was harvested before Pro registers its dokan_get_admin_settings_schema filters on init, so Pro-only sections never got listeners at all and their snapshots could serve stale values after a write.

Invalidation now listens on WordPress' generic added_option / updated_option / deleted_option hooks and decides membership against the snapshots actually held. No schema needed, and unmapped sections are covered too.

Fix 2 alone does not silence the notice — with the constructor lazy, the schema build simply moves down one frame into all(). Both changes are required.

Related Pull Request(s)

Closes

  • N/A — reported from debug.log on the branch.

How to test the changes in this Pull Request:

  1. Set WP_DEBUG and WP_DEBUG_LOG to true.
  2. Check out this branch with Dokan Pro active and load any front-end page.
  3. Before: wp-content/debug.log gains two _load_textdomain_just_in_time notices for the dokan-lite domain per request.
  4. After: no dokan-lite notices. (Any remaining _load_textdomain_just_in_time traces come from dokan-pro/includes/Module.php:169 on the dokan domain — a separate issue in that repo, normally hidden because Pro suppresses its own doing_it_wrong_trigger_error output.)
  5. Confirm the vendor dashboard still behaves: with vendor_layout_style = latest the React layout and its assets load; with legacy the theme template is used and no dashboard assets are enqueued.

Test coverage

Four new tests, each confirmed failing before the fix:

  • LegacySettingsRepositoryTest::test_constructor_does_not_build_the_settings_schema
  • LegacySettingsRepositoryTest::test_write_to_unmapped_section_flushes_its_snapshot
  • FullWidthVendorLayoutTest::test_register_hooks_does_not_read_settings
  • FullWidthVendorLayoutTest::test_layout_setting_is_read_when_a_callback_runs

Full PHPUnit run: 440 tests, 7 failures — the identical set before and after this change (Commission ×4, OrderStatusChange ×2, WithdrawChargeTransformer ×1), verified by re-running the suite against the unmodified sources on the same environment. No regressions. PHPCS clean on all changed files.

Changelog entry

Fix: translations no longer load before init

Reading a Dokan setting builds the admin settings schema through the legacy settings bridge, and that schema is translated. Two call sites did this before the init action, where the plugin's text domain is loaded — the vendor layout shortcode read its setting during hook registration on plugins_loaded, and the legacy settings repository built the whole schema in its constructor just to work out which options to watch. WordPress 6.7 and later flags this with a _load_textdomain_just_in_time notice, which was written to debug.log twice on every request, and the redundant schema build cost time on front-end requests that only needed a single option. Both call sites now defer the read until init or later, and cache invalidation no longer needs the schema at all.

Before Changes

debug.log, two entries per request:

[24-Jul-2026 09:12:25 UTC] PHP Notice:  Function _load_textdomain_just_in_time was called incorrectly.
Translation loading for the dokan-lite domain was triggered too early. This is usually an indicator for
some code in the plugin or theme running too early. Translations should be loaded at the init action or
later. (This message was added in version 6.7.0.) in wp-includes/functions.php on line 6170

After Changes

No dokan-lite entries — verified across repeated bootstraps on a Pro-active install.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 32c67992-7b20-47ea-94fe-e6078b26d264

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/settings-textdomain-loaded-too-early

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.

@kzamanbd

Copy link
Copy Markdown
Contributor Author

Pro companion: getdokan/dokan-pro#5983 — same root cause, fixes the four Pro modules that read settings in their constructors. Merge this Lite PR first.

@kzamanbd
kzamanbd requested a review from mrabbani July 27, 2026 05:09
@kzamanbd kzamanbd self-assigned this Jul 27, 2026
Reading any Dokan setting now walks the legacy settings bridge, which
builds the full admin settings schema — and that schema is translated.
`FullWidthVendorLayout::register_hooks()` read `vendor_layout_style` on
`plugins_loaded`, so `esc_html__()` ran before `init`, where
`load_plugin_textdomain()` is registered. WordPress 6.7+ answers that
with a `_load_textdomain_just_in_time` doing-it-wrong notice, logged
twice on every request.

Register the layout hooks unconditionally and move the gate into the
callbacks via `is_latest_layout()`. All three run on `init` or later,
and the repository memoizes the section snapshot, so the deferred read
costs nothing.

Also drop the eager schema build from `LegacySettingsRepository`'s
constructor. It called `known_sections()` purely to learn which legacy
`dokan_*` options to bind flush listeners to, which forced a full schema
build on construction — on every front-end request that read a single
option. Worse, that map was harvested before Pro registers its schema
filters on `init`, so Pro-only sections never got listeners and their
snapshots could go stale. Invalidation now listens on the generic
`added_option` / `updated_option` / `deleted_option` hooks and decides
membership against the snapshots actually held, which needs no schema
and covers unmapped sections too.
@kzamanbd
kzamanbd force-pushed the fix/settings-textdomain-loaded-too-early branch from 814eed3 to e098072 Compare July 30, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant