Skip to content

Commit a23366b

Browse files
committed
Compare MDI widget option visibility
1 parent 3847b72 commit a23366b

3 files changed

Lines changed: 73 additions & 8 deletions

File tree

packages/runtime-cloudflare/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ The WordPress server corpus is a separate deployment artifact, never a Worker-mo
2222

2323
The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work.
2424

25-
The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` calls `get_option()` once for every fixed core widget option id base without registering a sidebar or returning option values. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input.
25+
The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input.
2626

2727
## Verification
2828

packages/runtime-cloudflare/src/worker.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise<Response>
556556
}
557557
}
558558

559-
if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"].includes(phase)) {
559+
if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) {
560560
const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket)
561561
try {
562562
const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim()
@@ -628,7 +628,7 @@ function wordpressProbeCode(phase: string): string {
628628
return "<?php require '/wordpress/wp-load.php'; echo json_encode(['siteUrl' => get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);"
629629
}
630630

631-
const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"]
631+
const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"]
632632
if (widgetPhases.includes(phase)) {
633633
return `<?php
634634
$settings_path = '/wordpress/wp-settings.php';
@@ -708,6 +708,24 @@ function wp_codebox_widgets_probe_register_selected($class_names) {
708708
$registered[$class_name]->_register();
709709
}
710710
}
711+
function wp_codebox_widgets_probe_array_dimensions($value) {
712+
if (!is_array($value)) return 0;
713+
$dimensions = 1;
714+
foreach ($value as $item) $dimensions = max($dimensions, 1 + wp_codebox_widgets_probe_array_dimensions($item));
715+
return $dimensions;
716+
}
717+
function wp_codebox_widgets_probe_get_settings($class_names) {
718+
$registered = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? $GLOBALS['wp_widget_factory']->widgets : array();
719+
$completed = array();
720+
$settings_shapes = array();
721+
foreach ($class_names as $class_name) {
722+
if (!isset($registered[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name);
723+
$settings = $registered[$class_name]->get_settings();
724+
$completed[] = $class_name;
725+
$settings_shapes[$class_name] = array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings));
726+
}
727+
return array('completedClassNames' => $completed, 'settingsShapes' => $settings_shapes);
728+
}
711729
$direct_groups = array(
712730
'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'),
713731
'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'),
@@ -717,19 +735,53 @@ $direct_groups = array(
717735
'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'),
718736
);
719737
$widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text');
738+
$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block');
739+
$get_settings_groups = array(
740+
'mdi-widgets-get-settings' => $widget_classes,
741+
'mdi-widgets-get-settings-first' => array_slice($widget_classes, 0, 10),
742+
'mdi-widgets-get-settings-second' => array_slice($widget_classes, 10),
743+
);
720744
if ($phase === 'mdi-widgets-callbacks') {
721745
echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => wp_codebox_widgets_probe_inventory('widgets_init'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true)));
722746
return;
723747
}
724748
$removed_init = wp_codebox_widgets_probe_remove_callbacks('init', array('wp_widgets_init'), false);
725749
$memory_before = memory_get_usage(true);
726750
if ($phase === 'mdi-widgets-option-reads') {
727-
foreach ($widget_option_id_bases as $id_base) get_option('widget_' . $id_base);
728-
echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'optionCount' => count($widget_option_id_bases), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true)));
751+
$get_option_found = array();
752+
$get_option_missing = array();
753+
$sqlite_present = array();
754+
$sqlite_missing = array();
755+
foreach ($widget_option_id_bases as $id_base) {
756+
$option_name = 'widget_' . $id_base;
757+
if (get_option($option_name, false) === false) $get_option_missing[] = $option_name;
758+
else $get_option_found[] = $option_name;
759+
$row_present = $wpdb->get_var($wpdb->prepare("SELECT 1 FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $option_name));
760+
if ($row_present === '1') $sqlite_present[] = $option_name;
761+
else $sqlite_missing[] = $option_name;
762+
}
763+
echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'getOption' => array('found' => $get_option_found, 'missing' => $get_option_missing), 'sqliteRows' => array('present' => $sqlite_present, 'missing' => $sqlite_missing), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true)));
729764
return;
730765
}
731766
$removed_widgets = array();
732767
wp_codebox_widgets_probe_register_defaults();
768+
if (isset($get_settings_groups[$phase])) {
769+
$settings_probe = wp_codebox_widgets_probe_get_settings($get_settings_groups[$phase]);
770+
echo json_encode(array_merge(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true), $settings_probe));
771+
return;
772+
}
773+
if ($phase === 'mdi-widgets-register-one') {
774+
$class_name = 'WP_Widget_Text';
775+
$option_name = 'widget_text';
776+
$instance_number = 2;
777+
if (!isset($GLOBALS['wp_widget_factory']->widgets[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name);
778+
update_option($option_name, array($instance_number => array('title' => 'Widget probe')));
779+
$widget = $GLOBALS['wp_widget_factory']->widgets[$class_name];
780+
$settings = $widget->get_settings();
781+
$widget->_register_one($instance_number);
782+
echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'widgetClassName' => $class_name, 'preloadedOptionName' => $option_name, 'getSettingsShape' => array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings)), 'registerOneCompleted' => true));
783+
return;
784+
}
733785
if (isset($direct_groups[$phase])) {
734786
$removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array(), true);
735787
wp_codebox_widgets_probe_register_selected($direct_groups[$phase]);

tests/cloudflare-runtime.test.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi
178178
"mdi-widgets-direct-block",
179179
"mdi-widgets-direct-custom-html",
180180
"mdi-widgets-option-reads",
181+
"mdi-widgets-get-settings",
182+
"mdi-widgets-get-settings-first",
183+
"mdi-widgets-get-settings-second",
184+
"mdi-widgets-register-one",
181185
]) assert.match(worker, new RegExp(`"${phase}"`))
182186

183187
assert.match(worker, /\$needle = "do_action\( 'init' \);"/)
@@ -201,10 +205,19 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi
201205
assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/)
202206
assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/)
203207
assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/)
204-
assert.match(worker, /foreach \(\$widget_option_id_bases as \$id_base\) get_option\('widget_' \. \$id_base\)/)
208+
assert.match(worker, /if \(get_option\(\$option_name, false\) === false\) \$get_option_missing\[\] = \$option_name/)
209+
assert.match(worker, /\$wpdb->get_var\(\$wpdb->prepare\("SELECT 1 FROM \{\$wpdb->options\} WHERE option_name = %s LIMIT 1", \$option_name\)\)/)
210+
assert.match(worker, /'getOption' => array\('found' => \$get_option_found, 'missing' => \$get_option_missing\)/)
211+
assert.match(worker, /'sqliteRows' => array\('present' => \$sqlite_present, 'missing' => \$sqlite_missing\)/)
212+
assert.match(worker, /function wp_codebox_widgets_probe_get_settings\(\$class_names\)/)
213+
assert.match(worker, /\$settings = \$registered\[\$class_name\]->get_settings\(\)/)
214+
assert.match(worker, /'mdi-widgets-get-settings-first' => array_slice\(\$widget_classes, 0, 10\)/)
215+
assert.match(worker, /'mdi-widgets-get-settings-second' => array_slice\(\$widget_classes, 10\)/)
216+
assert.match(worker, /\$widget->_register_one\(\$instance_number\)/)
217+
assert.match(worker, /update_option\(\$option_name, array\(\$instance_number => array\('title' => 'Widget probe'\)\)\)/)
205218
assert.match(worker, /'classNamesAttempted' => \$direct_groups\[\$phase\]/)
206-
assert.match(worker, /'optionCount' => count\(\$widget_option_id_bases\)/)
207-
assert.doesNotMatch(worker, /get_option\('widget_' \. \$id_base\).*json_encode/)
219+
assert.doesNotMatch(worker, /json_encode\(\$settings\)/)
220+
assert.doesNotMatch(worker, /json_encode\(get_option/)
208221
assert.match(worker, /"rest_api_init"/)
209222
assert.match(worker, /"create_initial_taxonomies"/)
210223
assert.match(worker, /"wp_create_initial_post_meta"/)

0 commit comments

Comments
 (0)