Skip to content

fix(domain): skip async enqueue of wu_add_domain/wu_add_subdomain when no listener is hooked#1232

Merged
superdav42 merged 1 commit into
mainfrom
fix/skip-unhooked-domain-async
May 20, 2026
Merged

fix(domain): skip async enqueue of wu_add_domain/wu_add_subdomain when no listener is hooked#1232
superdav42 merged 1 commit into
mainfrom
fix/skip-unhooked-domain-async

Conversation

@superdav42

@superdav42 superdav42 commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Domain_Manager unconditionally enqueued the wu_add_domain and wu_add_subdomain Action Scheduler jobs on every site creation and custom-domain change. Those hooks are extension points consumed only by host‑provider integration modules (Cloudflare, cPanel, Hostinger, RunCloud, Hestia, Plesk, BunnyCDN, etc.) — each one registers its listener inside register_hooks() only when the integration is enabled.

In environments where no host integration is active, Action Scheduler ran each enqueued job with zero callbacks and logged "no callbacks are registered" on every site/domain operation. This was observed against every site created since 2026‑05‑08 (blogs 70–80) on an install with no host integration enabled.

Site creation and the wu_async_process_domain_stage DNS/SSL chain were never affected — domain stage progression has its own dedicated listener (class-domain-manager.php:155), which is why stage=done still completes.

Fix

Guard both enqueue sites with has_action(...):

  • inc/managers/class-domain-manager.php:339 (subdomain, inside handle_site_created)
  • inc/managers/class-domain-manager.php:776 (domain, inside send_domain_to_host)

Host providers register their listeners at plugin init, well before Action Scheduler claims async jobs, so has_action() is authoritative at enqueue time. Any enabled integration continues to receive both hooks as before.

Why not remove the hooks?

They are not vestigial. The Base_Host_Provider::register_hooks() method at inc/integrations/host-providers/class-base-host-provider.php:361,371 plus 17 capability modules under inc/integrations/providers/*/class-*-domain-mapping.php all subscribe. Removing the hooks would break every host integration.

Tests

Four new regression tests in tests/WP_Ultimo/Managers/Domain_Manager_Test.php:

  • test_send_domain_to_host_skips_enqueue_without_listener — asserts no action is queued when nothing is hooked to wu_add_domain.
  • test_send_domain_to_host_enqueues_when_listener_registered — asserts the action is queued with the expected args when a listener exists.
  • test_handle_site_created_skips_subdomain_enqueue_without_listener — same pair for wu_add_subdomain.
  • test_handle_site_created_enqueues_subdomain_when_listener_registered — same pair for wu_add_subdomain.

Each test snapshots $GLOBALS['wp_filter'][$hook], mutates it, asserts the precondition (has_action(...)), exercises the manager method, and queries Action Scheduler directly via wu_get_scheduled_actions() with status STATUS_PENDING and exact args matching. Original listeners are restored in finally.

Verification

vendor/bin/phpunit --filter Domain_Manager_Test --no-coverage
OK (137 tests, 319 assertions)

vendor/bin/phpcs inc/managers/class-domain-manager.php
# clean

vendor/bin/phpstan analyse inc/managers/class-domain-manager.php tests/WP_Ultimo/Managers/Domain_Manager_Test.php
# [OK] No errors

Out of scope

The same "no callbacks are registered" warning also appears for fetch_patterns (WordPress core block-pattern cache prime) and wc-admin_process_pending_orders_batch (WooCommerce Admin order stats backfill). Both are external to this plugin and not addressed here.


aidevops.sh v3.17.8 plugin for OpenCode v1.15.5 with claude-sonnet-4-20250514 spent 3h 34m on this as a headless worker.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented unnecessary background tasks from executing when no integrations are configured to handle domain operations, reducing system overhead and log clutter.

Review Change Stack

…n no listener is hooked

The Domain_Manager unconditionally enqueued the 'wu_add_domain' and
'wu_add_subdomain' Action Scheduler jobs on every site creation and
custom-domain change. Those hooks are extension points consumed only
by host-provider integration modules (Cloudflare, cPanel, Hostinger,
RunCloud, Hestia, Plesk, BunnyCDN, etc.) — each one registers its
listener inside register_hooks() only when the integration is enabled.

In environments where no host integration is active, Action Scheduler
ran each enqueued job with zero callbacks and logged 'no callbacks are
registered' on every site/domain operation. Site creation and the
wu_async_process_domain_stage DNS/SSL chain were never affected by
this — the domain stage progression has its own dedicated listener
(class-domain-manager.php:155).

Guard both enqueue sites with has_action(...). Host providers register
their listeners at plugin init, well before Action Scheduler claims
async jobs, so has_action() is authoritative at enqueue time. Any
enabled integration continues to receive both hooks as before.

Adds four regression tests covering both the skip path (no listener
registered) and the enqueue path (listener registered) for each hook.
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79fba15c-76ca-4bcc-b276-7babe5ff9188

📥 Commits

Reviewing files that changed from the base of the PR and between 9b0762f and ed44ddd.

📒 Files selected for processing (2)
  • inc/managers/class-domain-manager.php
  • tests/WP_Ultimo/Managers/Domain_Manager_Test.php

📝 Walkthrough

Walkthrough

Domain_Manager now enqueues async actions wu_add_domain and wu_add_subdomain only when integration callbacks are registered, eliminating Action Scheduler jobs with no listeners. Comprehensive regression tests verify both skip-enqueue and enqueue paths for both actions.

Changes

Conditional async action enqueueing with regression tests

Layer / File(s) Summary
Action enqueue conditional guards
inc/managers/class-domain-manager.php
handle_site_created() and send_domain_to_host() each guard async action enqueueing with has_action() checks, preventing scheduler runs when no integration callbacks are registered.
Regression test coverage for conditional enqueueing
tests/WP_Ultimo/Managers/Domain_Manager_Test.php
Four new test methods verify both skip-enqueue (no listener) and enqueue (listener present) paths for wu_add_domain and wu_add_subdomain, with deterministic hook state management and Action Scheduler cleanup.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • Ultimate-Multisite/ultimate-multisite#337: Directly affects Plesk domain-mapping integration hook registration for domain/subdomain lifecycle events in relation to the conditional enqueue guards introduced here.

Poem

🐰 A action that waits for a listener to call,
No empty jobs left scattered at all!
Tests verify guards both empty and full,
Domain events now efficient and dull.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding guards to skip async enqueue of two domain-related hooks when no listeners are registered.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/skip-unhooked-domain-async

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 and usage tips.

@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@superdav42 superdav42 merged commit 52e5b71 into main May 20, 2026
11 checks passed
@superdav42

Copy link
Copy Markdown
Collaborator Author

Summary

Domain_Manager unconditionally enqueued the wu_add_domain and wu_add_subdomain Action Scheduler jobs on every site creation and custom-domain change. Those hooks are extension points consumed only by host‑provider integration modules (Cloudflare, cPanel, Hostinger, RunCloud, Hestia, Plesk, BunnyCDN, etc.) — each one registers its listener inside register_hooks() only when the integration is enabled.
In environments where no host integration is active, Action Scheduler ran each enqueued job with zero callbacks and logged "no callbacks are registered" on every site/domain operation. This was observed against every site created since 2026‑05‑08 (blogs 70–80) on an install with no host integration enabled.
Site creation and the wu_async_process_domain_stage DNS/SSL chain were never affected — domain stage progression has its own dedicated listener (class-domain-manager.php:155), which is why stage=done still completes.

Fix

Guard both enqueue sites with has_action(...):

  • inc/managers/class-domain-manager.php:339 (subdomain, inside handle_site_created)
  • inc/managers/class-domain-manager.php:776 (domain, inside send_domain_to_host)
    Host providers register their listeners at plugin init, well before Action Scheduler claims async jobs, so has_action() is authoritative at enqueue time. Any enabled integration continues to receive both hooks as before.

Why not remove the hooks?

They are not vestigial. The Base_Host_Provider::register_hooks() method at inc/integrations/host-providers/class-base-host-provider.php:361,371 plus 17 capability modules under inc/integrations/providers/*/class-*-domain-mapping.php all subscribe. Removing the hooks would break every host integration.

Tests

Four new regression tests in tests/WP_Ultimo/Managers/Domain_Manager_Test.php:

  • test_send_domain_to_host_skips_enqueue_without_listener — asserts no action is queued when nothing is hooked to wu_add_domain.
  • test_send_domain_to_host_enqueues_when_listener_registered — asserts the action is queued with the expected args when a listener exists.
  • test_handle_site_created_skips_subdomain_enqueue_without_listener — same pair for wu_add_subdomain.
  • test_handle_site_created_enqueues_subdomain_when_listener_registered — same pair for wu_add_subdomain.
    Each test snapshots $GLOBALS['wp_filter'][$hook], mutates it, asserts the precondition (has_action(...)), exercises the manager method, and queries Action Scheduler directly via wu_get_scheduled_actions() with status STATUS_PENDING and exact args matching. Original listeners are restored in finally.

Verification

vendor/bin/phpunit --filter Domain_Manager_Test --no-coverage
OK (137 tests, 319 assertions)
vendor/bin/phpcs inc/managers/class-domain-manager.php
# clean
vendor/bin/phpstan analyse inc/managers/class-domain-manager.php tests/WP_Ultimo/Managers/Domain_Manager_Test.php
# [OK] No errors

Out of scope

The same "no callbacks are registered" warning also appears for fetch_patterns (WordPress core block-pattern cache prime) and wc-admin_process_pending_orders_batch (WooCommerce Admin order stats backfill). Both are external to this plugin and not addressed here.


aidevops.sh v3.17.8 plugin for OpenCode v1.15.5 with claude-sonnet-4-20250514 spent 3h 34m on this as a headless worker.


Merged via PR #1232 to main.
Merged by deterministic merge pass (pulse-wrapper.sh).

@github-actions

Copy link
Copy Markdown

Performance Test Results

Performance test results for a4269fb are in 🛎️!

Note: the numbers in parentheses show the difference to the previous (baseline) test run. Differences below 2% or 0.5 in absolute values are not shown.

URL: /

Run DB Queries Memory Before Template Template WP Total LCP TTFB LCP - TTFB
0 41 37.79 MB 907.00 ms (+57.00 ms / +6% ) 151.50 ms (-4.50 ms / -3% ) 1070.50 ms (+29.00 ms / +3% ) 2058.00 ms 1984.25 ms 80.85 ms (+4.10 ms / +5% )
1 56 49.14 MB 971.50 ms 148.00 ms (+3.00 ms / +2% ) 1125.00 ms 2132.00 ms 2062.30 ms 73.70 ms

@superdav42 superdav42 added the review-feedback-scanned Merged PR already scanned for quality feedback label May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-feedback-scanned Merged PR already scanned for quality feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant