-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Frontend requests can execute admin blueprint data-options@ callbacks during theme config joinDefaults() #4048
Description
Summary
Regular frontend requests can end up executing admin-only blueprint callbacks such as \Grav\Plugin\AdminPlugin::themeOptions() while loading theme configuration.
This does not look like an admin plugin bug by itself. The callback is declared for an admin form field, but it gets executed because Grav core loads global config/plugin/theme blueprints during config/theme default merging on the frontend.
The performance impact seems small but real on uncached/dev requests. In my profile it showed up as a few milliseconds, so this feels more like an architecture / boundary issue than a major performance regression.
Why this looks unexpected
data-options@ is documented as a dynamic blueprint mechanism for form fields / dynamic field values:
So the callback itself makes sense for Admin forms. What looks surprising is that the same dynamic callback is executed during a normal frontend request.
Relevant code path
Admin blueprint declares a dynamic callback for a select field:
data-options@: '\Grav\Plugin\AdminPlugin::themeOptions'- https://github.com/getgrav/grav-plugin-admin/blob/develop/blueprints.yaml#L354-L360
The callback itself is here:
On the Grav side, the frontend theme configuration path does:
Themes::configure()- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Themes.php#L277-L283
which calls:
loadConfiguration()- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Themes.php#L331-L335
which calls:
Config->joinDefaults("themes.{current-theme}", $themeConfig)- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Themes.php#L333-L334
joinDefaults() in Data uses blueprints when an existing value is present:
ConfigServiceProvider wires config blueprints lazily, but when they are resolved it loads:
blueprints://configplugins://themes://- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Service/ConfigServiceProvider.php#L86-L107
- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Service/ConfigServiceProvider.php#L131-L136
Then blueprint dynamic handlers are initialized/executed here:
Blueprint::init()- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Data/Blueprint.php#L141-L184
Blueprint::dynamicData()- https://github.com/getgrav/grav/blob/develop/system/src/Grav/Common/Data/Blueprint.php#L426-L460
So on a frontend request, loading theme defaults can indirectly execute admin blueprint data-options@ callbacks.
Reproduction
- Install Grav with Admin plugin enabled.
- Make a normal frontend request to any page.
- Profile the request or place a breakpoint in:
\Grav\Plugin\AdminPlugin::themeOptions()- https://github.com/getgrav/grav-plugin-admin/blob/develop/admin.php#L1332-L1345
- Observe that it can be hit during frontend config/theme loading, even though no Admin page is being rendered.
Expected behavior
One of these would seem more appropriate:
- Admin form callbacks such as blueprint
data-options@should not be eagerly executed on regular frontend requests. - Or frontend config/theme default merging should avoid resolving blueprint dynamic form data that is only needed for Admin UI.
Actual behavior
A regular frontend request can execute admin-only dynamic blueprint callbacks because global config/plugin/theme blueprints are loaded and initialized during theme config merging.
Notes
I do understand that blueprints are not admin-only in general, because they are also used for defaults / config merging. The issue here is narrower: dynamic Admin form callbacks appear to be executed as a side effect of frontend config processing.